最长公共子序列(LCS)

来源:互联网 发布:数控机床编程步骤 编辑:程序博客网 时间:2024/06/11 10:50

定义:

最长公共子序列,英文缩写为LCS(Longest Common Subsequence)。其定义是,一个序列 S ,如果分别是两个或多个已知序列的子序列,且是所有符合此条件序列中最长的,则 S 称为已知序列的最长公共子序列。而最长公共子串(要求连续)和最长公共子序列是不同的


例如  下面两个单词中颜色标记出来的 data 就是 didactical  advantage 的 最长公共子序列, 长度为4;

didactical
advantage 


求解:

一、首先我们用递归思想设计一个正确、可行的解。

①如果其中一个序列为空,那么最长公共子序列一定为空,长度为 0 。

②我们比较这两个序列的最后一个字母,如果相同,我们就可以将可以将问题规模缩小1位。

例如:

appl                                                                     appl

abstpdple  最后一个相同 , 我们就可以进而求     abstpdpl 的最长公共子序列, 当然这时候要记录下已经有了的公共部分。


③如果最后一位不同怎么办?

例如

ap

abstpd , 可以想到 ,这时候问题分为了两个部分:

        a                                                ap

求解 abstpd 的最长公共子序列 ,和 abstpd 的最长公共子序列

因为最后一个字符不一样,所以我们必定要舍弃两个序列中其中一个的最后一位。显然,我们需要这两个问题中公共子序列更长的那一个。


这样,整个递归算法就算描述完了。


实现的代码在文章最后给出,最好自己直接按照上述描述敲出来代码。


二、寻找优化:动态规划的思想。

我们可以简单的对上面递归的算法进行一些时间分析

最好的情况,也就是不出现③中情况的时候,只需要O(min(lena,lenb)).

然而一旦出现③,那么问题就会分解为两个问题,更糟糕的是,这两个问题的子问题可能雷同!

如图可以看到,两个子问题的子问题可能雷同,将造成大量的重复计算。

最坏的情况将达到 C(lena,lena+lenb)(组合数符号), 当lena == lenb 时 ,大致为 O(2^n)。

时间复杂度达到了指数级别!


这里我们运用动态规划的思想,刚才递归是自顶向下,我们自底向上的去求解问题。

首先初始化dp数组的第一行和第一列为0;




如图所示,我们可以一行一行的填写这张表,运用上面递归中所讲的方法,

如果遇到相同的字符,dp[i][j] = dp[i-1][j-1] + 1 ,

如果遇到不同的字符 dp[i][j] =max(dp[i-1][j] , dp[i][j-1]) .

这样每个空格只用遍历一次就能求出所有的结果,dp[lena][lenb]就是结果啦。


源代码:

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>using namespace std;const int maxn = 600;int dp[maxn][maxn];//递归版本的LCSint fun(string a,string b,int count){int lena = a.length();int lenb = b.length();if(lena == 0 || lenb == 0)return count;string tmpa,tmpb;tmpa = a.substr(0,lena-1);tmpb = b.substr(0,lenb-1);if(a[lena-1] == b[lenb-1]){return fun(tmpa,tmpb,count+1);}else {return max(fun(tmpa,b,count),fun(a,tmpb,count));}}//动态规划LCSvoid dynamic(string a,string b){int lena = a.length();int lenb = b.length();for(int i=0;i<=lena;i++){dp[i][0] = 0;}for(int i=0;i<=lenb;i++){dp[0][i] = 0;}for(int i=0;i<lena;i++){for(int j=0;j<lenb;j++){if(a[i] == b[j]){dp[i+1][j+1] = dp[i][j] + 1;}else{dp[i+1][j+1] = max(dp[i+1][j] , dp[i][j+1]);}}}printf("%d\n",dp[lena][lenb]);}void print(){//打印dp数组for(int i=0;i<=lena;i++){for(int j=0;j<=lenb;j++){printf("%d ",dp[i][j]);}puts("");}}int main(){string a,b;while(getline(cin,a)){getline(cin,b);dynamic(a,b); //动态规划// int ans = fun(a,b,0); //递归版本// printf("%d\n",ans);}return 0;}






3 0
原创粉丝点击