最长公共子序列(LCS)

原创 2016年08月30日 22:09:40

本来这篇文章是想直接转载过来一篇,然后看看就行了,但是个人总觉得看别人的不如自己动手写过的理解好,所以还是决定把理解的过程记录下来。

LCS问题:

首先先知道LCS问题,这有两种:
1.Longest Common Substiring —- 最长公共子串
2.Longest Common Sequence —- 最长公共子序列

这两者的区别是:前者必须是原字符串中连续的一段
后者可以是在原字符串中随意抽取的一些字符串拼凑成的字符串,只需要遵守顺序即可

也就是说:子串字符的位置必须是连续的,子序列不必连续

求两个子串的最长公共子序列

1.暴力法:

假设两个字符串str1.length = m ,str2.length = n

找出m中所有的子序列,找出n中所有的子序列,然后两者中所有元素一一对比
找出过程需要2^m 和 2^n 的时间复杂度,再一一对比。

即时间复杂度是:O(2^m * 2^n)
空间复杂度:O(2^m + 2^n)

2.动态规划

这种类型的题目考的就是动态规划

这里引用July的一段分析,先分析完再来做题:
事实上,最长公共子序列问题也有最优子结构性质。

Xi=﹤x1,⋯,xi﹥即X序列的前i个字符 (1≤i≤m)(前缀)
Yj=﹤y1,⋯,yj﹥即Y序列的前j个字符 (1≤j≤n)(前缀)

假定Z=﹤z1,⋯,zk﹥∈LCS(X , Y)。

  • 若Xm = Yn (最后一个字符相同),则不难用反证法证明:该字符必定是X与Y的任一最长公共子序列(设其长度为k)的最后一个字符,即是:Zk = Xm = Yn。这就得到:Zk - 1 ∈ LCS(Xm-1 , Yn-1),即Z的前缀Zk-1是Xm-1 与 Ym-1 的最长公共子序列。这样就将问题缩小了,并且可以根据这个递推不断往回:Xm-1 与 Yn-1 的LCS。( LCS(X,Y) 的长度等于LCS(Xm-1,Yn-1) 的长度+1
  • 若Xm ≠ Yn,则不难用反证法证明:要么Z ∈ LCS(Xm-1,Y),要么Z ∈ LCS(X,Yn-1)。由于Zk ≠ Xm 与 Zk ≠ Yn中,至少有一个必然成立,若Zx ≠ Xm则有 Z ∈ LCS(Xm-1,Y) ,类似的,若Zk ≠ Yn,则有Z ∈ LCS(X,Yn-1)。此时,问题转换成求Xm-1 与 Y的LCS,以及 X 与 Yn-1的LCS。LCS(X,Y) 的长度为:max( LCS(Xm-1 , Y) , LCS(X , Yn-1) )。

由于上述当Xm ≠ Yn的情况中,求LCS(Xm-1 , Y) 的长度与LCS(X , Yn-1)的长度,这两个问题不是相互独立的:两者都需要求LCS(Xm-1 , Yn-1)的长度。另外两个序列的LCS中包含了两个序列的前缀的LCS,到这里得出结论:问题具有最优子结构性质,考虑用动态规划法。

也就是说,解决这个LCS问题,你要求三个方面的东西:
1. LCS ( Xm-1,Yn-1 ) +1;
2. LCS ( Xm-1,Y ),LCS ( X,Yn-1 );
3. max ( LCS( Xm-1,Y ) ,LCS( X,Yn-1) )
如下图所示:
这里写图片描述

看完以上的专业的推导,现在来说说咱们自己的理解吧(乡土气息弥漫….),就做这个题目先来:

str1  : B D C A B A          长度为m = 6      
str2  : A B C B D A B        长度为n = 7
找出这两个字符串中的最长公共子序列
  1. 首先,我们申请一个二维数组dp[m] [n],用来存储两个str分别从1~m与1~n的所有长度的串对应的最长公共子序列的长度。
  2. 初始化一些已知的情况,比如:dp[0][0] = 0,dp[i] [0] = dp[0] [j],显而易见,当一个str长度为0,另一个长度无论你为多少,都没有公共子串,长度也就为0.
  3. 之后的值就取决于如上图中的另外两种情况,当str1[i] = str2[j] 的时候,就考虑对角上的前一个元素,在它的基础上+1,得到当前最大,当前元素的前一个对角元素是str1和str2长度都减去1的最优解(我们从开始就是这么定义的,相等在加1,不等就沿用左或上的最优,因为没有加1,那就找各自在对方不变的情况下回退一步的最优(一个长度减1,一个长度不变),即是最优);;在不等的时候,只需要沿用上一次比较中str1[i-1][j] 和 str2[i][j-1]中比较大的一个,意思就是保留之前子串比较中最优的那一个,因为还没有得到最新的最优嘛(不能+1)。那么无论怎么样,我们都得到了当前的最优结果,供下一次再来选择比较。

具体的过程如图下:
这里写图片描述

代码如下:

int Lcs(string s1,string s2){
        int m = s1.length();
        int n = s2.length();
        vector<vector<int> > res(n+1);
        for(int i = 0;i <= n;i++){
                res[i].resize(m+1);
        }
        for(int i = 0;i < m;i++){
                for(int j = 0; j < n;j++){
                        if(s1[i] == s2[j]){
                                res[i+1][j+1] = res[i][j] + 1;
                        }
                        else{
                                res[i+1][j+1] = max(res[i][j+1],res[i+1][j]);
                        }
                }
        }
        for(int i = 0;i <= m;i++){
                for(int j = 0;j <= n;j++){
                        cout<<res[i][j]<<" ";
                }
                cout<<endl;
        }
        cout<<endl;
        return res[m][n];
}

总结:

大概说完这个过程,起码我们能拿到两个字符串立马画出上面这张图吧。
动态规划这种拆分最优子问题的思想个人觉得还是比较难的,尤其是要将一个问题转化成dp,是一件很需要思想的事情,暂时自己的办法是多练,多写,多想,希望在某个时刻脑子开窍了动态规划清晰地像我未来老婆一样在我的脑子里浮现了,有什么错误还请指出,谢谢!

版权声明:本文为博主原创文章,未经博主允许不得转载。

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

1.基本概念       首先需要科普一下,最长公共子序列(longest common sequence)和最长公共子串(longest common substring)不是一回事儿。什么是子序...
  • hrn1216
  • hrn1216
  • 2016年05月29日 22:54
  • 36302

最长公共子序列(LCS)

1、LCS基本概念          子序列:一个序列X任意删除若干个字符得到新序列Z,则Z叫做X的子序列。例如Z=是X=B,C,B,D,A,B>的子序列,相当于删除A、B、A。          公...
  • DQ_DM
  • DQ_DM
  • 2015年04月14日 16:18
  • 5730

动态规划解最长公共子序列问题

动态规划法 经常会遇到复杂问题不能简单地分解成几个子问题,而会分解出一系列的子问题。简单地采用把大问题分解成子问题,并综合子问题的解导出大问题的解的方法,问题求解耗时会按问题规模呈幂级数增加。 为了节...
  • yysdsyl
  • yysdsyl
  • 2009年05月30日 21:28
  • 156474

最长公共子序列与最长公共子串(DP)

1. 问题描述 子串应该比较好理解,至于什么是子序列,这里给出一个例子:有两个母串 cnblogsbelong 比如序列bo, bg, lg在母串cnblogs与belong中都出现过并且出现顺序...

【动态规划】输出所有的最长公共子序列

上篇讲到使用动态规划可以在 θ(mn) 的时间里求出 LCS 的长度,本文将讨论如何输出最长公共子序列。 问题描述:给定两个序列,例如 X = “ABCBDAB”、Y = “BDCABA”,求它们的最...

动态规划:求最长公共子串/最长公共子序列

最长公共子序列和最长公共子串区别        最长公共子串(Longest Common Substring)与最长公共子序列(Longest Common Subsequence)的区别: 子串要...

最长公共子序列nlogn算法

求两个序列的最大公共子序列(LCS),普遍的动态规划算法时间复杂度为O(n^2),在两个序列的长度很长的时候运行时间过长。可以转化为最长上升子序列(LIS)来求,时间复杂度可以优化到nlogn。1.首...

算法导论第15章 动态规划-最长公共子序列

1、基本概念   一个给定序列的子序列就是该给定序列中去掉零个或者多个元素的序列。形式化来讲就是:给定一个序列X={x1,x2,……,xm},另外一个序列Z={z1、z2、……,zk},如果存在X的...

最长公共子序列(LCS问题)

先简单介绍下什么是最长公共子序列问题,其实问题很直白,假设两个序列X,Y,X的值是ACBDDCB,Y的值是BBDC,那么XY的最长公共子序列就是BDC。这里解决的问题就是需要一种算法可以快速的计算出这...

动态规划算法之最长公共子序列问题

一、问题描述 求两个字符序列的公共最长子序列。例如字符序列abcbdb和字符序列acbbabdbb的最长公共子序列为acbdb。 二、问题分析 用L[i][j]表示子序列xi和yj的最长公共子序列的长...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:最长公共子序列(LCS)
举报原因:
原因补充:

(最多只允许输入30个字)