動態規劃-最長公共子序列

公共子序列

給定兩個字符串m和n,如果它們的子串a和b內容相同,則稱a和b是m和n的公共子序列。子串中的字符在原字符串中不要求連續,只要保持原有相對順序即可。

例如,字符串“abcfbc”和“abfcab”,其中“abc”同時出現在兩個字符串中,因此“abc”是它們的公共子序列。

算法求解

動態規劃問題一般具備兩個特徵:

1) 最優子結構;

2) 重疊子問題;

設 X=(x1,x2,.....xn) 和 Y={y1,y2,.....ym} 是兩個序列,將 X 和 Y 的最長公共子序列記為LCS(X,Y)。

最優子結構

首先考慮X的最後一個元素和Y的最後一個元素。

1) 如果 xn=ym,即X的最後一個元素與Y的最後一個元素相同,這說明該元素一定位於公共子序列中。因此,現在只需要找:LCS(Xn-1,Ym-1)。

LCS(Xn-1,Ym-1)就是原問題的一個子問題。為什麼是最優的子問題?因為我們要找的是X

n-1 和 Ym-1 的最長公共子序列。

2) 如果xn != ym,因為序列X 和 序列Y 的最後一個元素不相等,那最後一個元素不可能是最長公共子序列中的元素。因此產生了兩個子問題:LCS(Xn-1,Ym) 和 LCS(Xn,Ym-1)。

LCS(Xn-1,Ym)表示:最長公共序列可以在(x1,x2,....x(n-1)) 和 (y1,y2,...yn)中找。

LCS(Xn,Ym-1)表示:最長公共序列可以在(x1,x2,....xn) 和 (y1,y2,...y(n-1))中找。

求解上面兩個子問題,得到的公共子序列誰最長,那誰就是 LCS(X,Y)。用數學表示就是:

LCS=max{LCS(Xn-1,Ym),LCS(X

n,Ym-1)}。

通過1)和2)可以把原問題轉化成三個規模更小的子問題。

重疊子問題

重疊子問題就是原問題轉化成子問題後,子問題中有相同的問題。

原問題是LCS(X,Y),子問題有LCS(Xn-1,Ym-1)、LCS(Xn-1,Ym)、LCS(Xn,Ym-1)。其中LCS(Xn-1,Ym) 就包含了LCS(Xn-1,Ym-1),因為當Xn-1和Ym的最後一個元素不相同時,我們又需要將LCS(Xn-1,Y

m)進行分解成:LCS(Xn-1,Ym-1) 和 LCS(Xn-2,Ym)。也就是說在子問題的繼續分解中,有些問題是重疊的。

它具有重疊子問題的性質,因此用遞歸來求解就太不划算了,因為採用遞歸,它重複地求解了子問題。

最長公共子序列的遞歸式如下:

動態規劃-最長公共子序列

c[i,j]表示:(x1,x2....xi) 和 (y1,y2...yj) 的最長公共子序列的長度。

算法實現

int findLCS(const std::string& A, const std::string& B) {

size_t n = A.size();

size_t m =B.size();

std::vector<:vector>> dp(n + 1, std::vector(m + 1, 0));

for (int i = 0; i < n; i++) {

for (int j = 0; j < m; j++) {

if (A[i] == B[j]) {

dp[i + 1][j + 1] = dp[i][j] + 1;

} else {

dp[i + 1][j + 1] = max(dp[i + 1][j], dp[i][j + 1]);

}

}

}

return dp[n][m];

}


分享到:


相關文章: