LCS(最长公共子序列)

Posted darkchii

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LCS(最长公共子序列)相关的知识,希望对你有一定的参考价值。

  这个问题很有意思,在生物应用中,经常需要比较两个(或多个)不同生物体的DNA片段。例如,某种生物的DNA可能为S1 = ACCGGTCGAGTGCGCGGAAGCCGGCCGAA,S2 = GTCGTTCGGAATGCCGTTGCTCTGTAAA。比较两个DNA串是想要确定它们之间的“相识度”,作为度量两种生物相近程度的指标。LCS就是寻找第三个串S3,S3满足定义:给定一个序列X = <x1,x2,...,xn>,另一个序列Y = <y1,y2,...ym>即存在一个严格递增的X的下标序列<i1,i2,...,im>,对所有j = 1,2,...,m,满足xij = zj。例如,Z = BCDB是ABCBDAB的子序列,对应的下标为[2,3,5,7]。

  如果用暴力求解则很容易就达到指数阶的运行时间,所以显然需要优化。通过分析发现LCS问题具有最优子结构性质,于是,考虑用动态规划是一个非常不错的方案。

  显然早就有许多人对此类问题做过很多工作,不管是理论上还是实际测试,都证明动态规划可以解决这一类型的问题,有时候甚至是最优方案。如何熟练使用动态规划的一大难点在于分析问题,要理解如何使用动态规划来解决问题,首先分析问题是否具有最优子结构是重要的,然后就是通过分析问题得到递归公式,只要得到了递归公式,我们就可以使用动态规划了。

  LCS问题也不例外,经过分析和查资料,我们知道了LCS得最优子结构定理:

  令X = <x1,x2,...,xn>和Y = <y1,y2,...ym>为两个序列,Z = <z1,z2,...,zk>为X和Y的任意LCS。

  • 如果xn = ym,则zk = xn = ym且Zk - 1是Xn - 1和Ym - 1的一个LCS;
  • 如果xn ≠ ym且zk ≠ xn,说明Z是Xn - 1和Y的一个LCS;
  • 如果xn ≠ ym且zk ≠ ym,说明Z是X和Ym的一个LCS;

  然后,我们开始建立最优解的递归式。定义dp[i][j]为Xi和Yj的LCS长度。显然的,当i = 0或j = 0时,LCS = 0。再结合最优子结构定理,可得到递归式:

                (    0;                              当 i = 0 或 j = 0;
dp[i][j] =      {    dp[i - 1][j - 1] + 1            当 i,j > 0 且 xi = yj;
                (    max(dp[i][j - 1], dp[i - 1][j]) 当 i,j > 0 且 xi != yj.

  得到递归式后,就可以根据公式写出递归算法或动态规划算法:

#include <iostream>
#include <string>
#include <vector>
#include <minmax.h>

class Solution {
public:
	int LongestCommonSubsequence(std::string s1, std::string s2) {
		if (s1.empty() || s2.empty())
			return 0;
		std::vector<std::vector<int> > dp(s1.size(), std::vector<int>(s2.size(), 0));
		int length = 0;
		for (int i = 1; i < dp.size(); i++) {
			int j = 1;
			for (; j < dp[0].size(); j++) {
				if (s1[i] == s2[j])
					dp[i][j] = dp[i - 1][j - 1] + 1;
				else
					dp[i][j] = max(dp[i][j - 1], dp[i - 1][j]);
				length = max(length, dp[i][j]);
			}
		}
		return length + 1;
	}
};

int main()
{
	Solution solve;
	std::string s1{"ABCBDAB"};
	std::string s2{"ABDCABA"};

	std::cout << solve.LongestCommonSubsequence(s1, s2) << std::endl;

	return 0;
}

  最后,类似的问题还有LIS(最长递增子序列)、LPS(最长回文子串)等等。

  参考资料:

    《算法导论》

以上是关于LCS(最长公共子序列)的主要内容,如果未能解决你的问题,请参考以下文章

每日一题-——最长公共子序列(LCS)与最长公共子串

最长公共子序列(LCS)

LeetCode(LCSி)最长公共子序列&变形应用

最长公共子序列 LCS

最长公共子序列(LCS)

最长公共子序列(LCS)