动态规划 最长公共子序列过程

明确什么是公共子序列

首先需要科普一下,最长公共子序列(longest common sequence)和最长公共子串(longest common substring)不是一回事儿。什么是子序列呢?即一个给定的序列的子序列,就是将给定序列中零个或多个元素去掉之后得到的结果。什么是子串呢?给定串中任意个连续的字符组成的子序列称为该串的子串。

  • 子序列是没有序的
  • 子串是有序的

一个简单的例子说明:
$s_1$ = “abcd”;这个字符串的子序列诸如 “a”,”ab”,”bc”,”ac”等 且拥有子串的数量是$2^n$个 其中 n 是字符串字符的个数。然而”ab”,”bc”,”abc”这些则称为子串,是按照一定顺序的

动态规划

求解LCS问题,不能使用暴力搜索方法。一个长度为n的序列拥有 2的n次方个子序列,它的时间复杂度是指数阶,太恐怖了。解决LCS问题,需要借助动态规划的思想。

动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有最优值的解。动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次。如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。我们可以用一个表来记录所有已解的子问题的答案。不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。这就是动态规划法的基本思路。

特征分析

解决LCS问题,需要把原问题分解成若干个子问题,所以需要刻画LCS的特征。

设A=“$a_0$,$a_1$,…,$a_m$”,B=“$b_0$,$b_1$,…,$b_n$”,且Z=“$z_0$,$z_1$,…,$z_k$”为它们的最长公共子序列。不难证明有以下性质:

  • 如果 $a_m$=$b_n$,则$z_k$=$a_m$=$b_n$,且“$z_0$,$z_1$,…,$z_(k-1)$”是”$a_0$,$a_1$,…,$a_(m-1)$”和“$b_0$,$b_1$,…,$b_(n-1)$”的一个最长公共子序列;

  • 如果 $a_m$ != $b_n$,则若$z_k$!=$a_m$,蕴涵“$z_0$,$z_1$,…,$z_k$”是“$a_0$,$a_1$,…,$a_(m-1)$”和“$b_0$,$b_1$,…,$b_n$”的一个最长公共子序列;

  • 如果$a_m$!=$b_n$,则若$z_k$!=$b_n$,蕴涵“$z_0$,$z_1$,…,$z_k$”是“$a_0$,$a_1$,…,$a_m$”和“$b_0$,$b_1$,…,$b_(n-1)$”的一个最长公共子序列。

用递推公式

例题分析

最长公共子串问题
给定两个字符串 $s_1$$s_2$…..$s_n$和$t_1$$t_2$……$t_n$。求出这两个字符串最长的公共子序列的长度。
限制条件: 1 <= n; m <= 1000

输入

n = 4
m = 4
s = “abcd”
t = “becd”

输出

3 (“becd”)

代码

    #include <iostream>  
    #include <cstdio>  
    #include <algorithm>  
    #include <cstring>  
    #include <string>  
    using namespace std;  
    string a,b;  
    int dp[1001][1001];  
    int solve(int al,int bl)  
    {  
        for(int i=0;i<al;i++)  
        {  
            for(int j=0;j<bl;j++)  
            {  
                if(a[i]==b[j])  
                {  
                    dp[i+1][j+1]=dp[i][j]+1;  
                }  
                else  
                {  
                    dp[i+1][j+1]=max(dp[i][j+1],dp[i+1][j]);  
                }  
            }  
        }  
        return dp[al][bl];  
    }  
    int main()  
    {  
        while(cin>>a>>b)  
        {  
            int al=a.length();  
            int bl=b.length();  
            printf("%d\n",solve(al,bl));  
        }  
        return 0;  
    }  

版权声明:本文为yjlblog原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:http://www.cnblogs.com/yjlblog/p/7875343.html