您的位置:首页 > 其它

动态规划之最长公共子序列 (LCS )

2017-02-08 00:12 330 查看
最长公共子序列和最长公共子串问题
【问题描述:】

假设有两个序列,序列A:1,3,5,4,2,6,8,7 序列B:1,4,8,6,7,5.

则序列AB的最长公共子序列 1487,1467。最长公共子序列是不连续的,最长公共子串是连续的。

子序列:一个序列A = a1,a2,……an,中任意删除若干项,剩余的序列叫做A的一个子序列。也可以认为是从序列A按原顺序保留任意若干项得到的序列。
请注意:子序列不是子集,它和原始序列的元素顺序是相关的。

【问题分析:】

    我们假设Ax为序列A的连续前x项的子序列,即a1a2a3….ax,By为序列B的前y项的子序列,用L(x,y)表示序列Ax,By的最长公共子序列的长度。

(1)  ax == by

我们令t = ax = by,则L(Ax,By)的最长公共子序列的最后一项一定是t,如果不是t,则一定存在比他更长的公共子序列。如果删掉最后一项t,则最长公共子序列为L(x-1,y-1)。为什么呢?,原理同上。所以推出dp[x][y] =dp[x-1][y-1]+1。

(2)  ax != by

仍然设L(Ax,By)的最后一项为t,当t== ax时,则t != by,所以在By序列的最后一项元素一定不是by,只能够是by前面的元素,也就是说L(Ax,By)和by一点关系都没有,即可得dp[x][y]=dp[x][y-1];同理可得dp[x-1][y];又因为不确定L(Ax,By)得最后一项是ax还是by,所以dp[x][y]=max(dp[x-1][y],dp[x][y-1]);

代码如下:

for(int i = 1; i <= strlen(a); i ++)
for(int j = 1; j <= strlen(b); j ++)
if(a[i] == b[j])
dp[i][j] = dp[i-1][j-1] + 1;
else
dp[i][j] = max(dp[i-1][j],dp[i][j-1]);


下面再说下关于输出最长公共子序列的问题,只需要用回溯进行标记输出即可。

LCSLength(int lena, int lenb){
for(int i = 1; i <= strlen(a); i ++)
for(int j = 1; j <= strlen(b); j ++)
if(a[i] == b[j]){
dp[i][j] = dp[i-1][j-1] + 1;
x[i][j] = 0;
}
else{
if(dp[i-1][j] > dp[i][j-1]){
dp[i][j] = dp[i-1][j];
x[i][j] = -1;
}
else{
dp[i][j] = dp[i][j-1];
x[i][j] = 1;
}
}.
}

PrintLCS(int lena, int lenb){
if(lena == 0 || lenb == 0)
return ;
if(x[lena][lenb] == 0){
PrintLCS(lena – 1,lenb - 1);
printf(“%c”,a[lena - 1]);
}
else if(x[lena][lenb] == -1)
PrintLCS(lena-1,lenb);
else
PrintLCS(lena,lenb-1);
}


最后再说下最长公共子串的求法,因为最长公共子串是连续的,所以如果ax!=by 则L(Ax,By) = 1,相等时的情况和最长公共子序列的情况一样。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: