1. 程式人生 > >LCS(最長公共子序列)

LCS(最長公共子序列)

rdquo 工作 dna abc sub 動態規劃 != 給定 似的

  這個問題很有意思,在生物應用中,經常需要比較兩個(或多個)不同生物體的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(最長公共子序列)