求最长公共子序列

最长公共子序列和最长公共子串区别

       最长公共子串(Longest Common Substring)与最长公共子序列(Longest Common Subsequence)的区别: 子串要求在原字符串中是连续的,而子序列则只需保持相对顺序一致,并不要求连续。例如X = {a, Q, 1, 1}; Y = {a, 1, 1, d, f}那么,{a, 1, 1}是X和Y的最长公共子序列,但不是它们的最长公共字串。

 

分析

        以 S1={1,3,4,5,6,7,7,8} S2={3,5,7,4,8,6,7,8,2} 为例。

        假如S1的最后一个元素S2的最后一个元素相等,那么S1 和 S2 的 LCS 就等于 {S1减去最后一个元素} 与 {S2减去最后一个元素} 的 LCS  再加上 S1和S2相等的最后一个元素。

        假如S1的最后一个元素 与 S2的最后一个元素不等(本例子就是属于这种情况),那么S1和S2的LCS就等于 : {S1减去最后一个元素} 与 S2 的LCS, {S2减去最后一个元素} 与 S1 的LCS 中的较大的那个序列。

 

递归公式

       我们可以发现,假设我需要求 a1 ... amb1 .. b(n-1)的LCS 和 a1 ... a(m-1)b1 .. bn的LCS,一定会递归地并且重复地把如a1... a(m-1)b1 ... b(n-1) 的 LCS 计算几次。所以我们需要一个数据结构来记录中间结果,避免重复计算。

        设X = {x1 ... xm}Y ={y1...yn}Xi = {x1 ... xi}Yj={y1... yj}。用一个二维数组C[ i ][ j ](i,j从1开始算)来记录中间结果可得递归公式如下:

 

计算方法

        还是以s1={1,3,4,5,6,7,7,8},s2={3,5,7,4,8,6,7,8,2}为例。

 

        图中的空白格子需要填上相应的数字(这个数字就是c[i,j]的定义,记录的LCS的长度值)。填的规则依据递归公式,简单来说:如果横竖(i,j)对应的两个元素相等,该格子的值 = c[i-1,j-1] + 1如果不等,取c[i-1,j] 和 c[i,j-1]的最大值

        首先初始化该表:第一行和第一列的元素全为0

然后,一行一行地从上往下填:

根据计算法则可得最后的结果如下图

最后所求值即为 c[i][j] (c[8][9])的值 5

 

构造LCS

       本文S1和S2的最LCS并不是只有1个,本文并不是着重讲输出两个序列的所有LCS,只是介绍如何通过上表,输出其中一个LCS。

       我们根据递归公式构建了上表,我们将从最后一个元素c[8][9]倒推出S1和S2的LCS

       c[8][9] = 5,且S1[8] != S2[9],所以倒推回去,c[8][9]的值来源于c[8][8]的值(因为c[8][8] > c[7][9])。

       c[8][8] = 5,  且S1[8] = S2[8], 所以倒推回去,c[8][8]的值来源于 c[7][7]。

       以此类推,如果遇到S1[i] != S2[j] ,且c[i-1][j] = c[i][j-1] 这种存在分支的情况,这里请都选择一个方向(之后遇到这样的情况,也选择相同的方向)。

第一种结果为:

这就是倒推回去的路径,棕色方格为相等元素,即 LCS = {3,4,6,7,8},这是其中一个结果。

如果如果遇到S1[i] != S2[j] ,且c[i-1][j] = c[i][j-1] 这种存在分支的情况,选择另一个方向,会得到另一个结果。

 

即LCS ={3,5,7,7,8}。

代码:


int LCS_length(string& str1, string& str2)
{
	int i, j;
	int **lcs = new int*[str1.size() + 1];
	for (i = 0; i <= str1.size(); i++)
	{
		lcs[i] = new int[str2.size() + 1]();
	}
	for (i = 1; i <= str1.size(); i++)
	{
		for (j = 1; j <= str2.size(); j++)
		{
			if (str1[i - 1] == str2[j - 1])
			{
				lcs[i][j] = lcs[i - 1][j - 1] + 1;
			}
			else
			{
				lcs[i][j] = max(lcs[i - 1][j], lcs[i][j - 1]);
			}
		}
	}
	return lcs[str1.size()][str2.size()];
}

 

 

参考资料

https://blog.csdn.net/hrn1216/article/details/51534607

https://blog.csdn.net/u013074465/article/details/45392687

 

 

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值