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

 

最长公共子序列和最长公共子串问题

【问题描述:】

假设有两个序列,序列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,相等时的情况和最长公共子序列的情况一样。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值