1. 程式人生 > >動態規劃 最長公共子序列

動態規劃 最長公共子序列

一個 then mda 偽代碼 n-2 msu csdn static 證明

最長公共子序列(LCS)問題

下面通過一個具體的例子來學習動態規劃方法 —— 最長公共子序列問題。

最長公共子串(Longest Common Substring)最長公共子序列(Longest Common Subsequence)的區別: 子串要求在原字符串中是連續的,而子序列則只需保持相對順序,並不要求連續。

問題描述:給定兩個序列:X[1...m]Y[1...n],求在兩個序列中同時出現的最長子序列的長度。

假設 X 和 Y 的序列如下:

      X[1...m] = {A, B, C, B, D, A, B}

      Y[1...n] = {B, D, C, A, B, A}

可以看出,X 和 Y 的最長公共子序列有 “BDAB”、“BCAB”、“BCBA”,即長度為4。

1) 窮舉法

可能很多人會想到用窮舉法來解決這個問題,即求出 X 中所有子序列,看 Y 中是否存在該子序列。

  X 有多少子序列 —— 2m

  檢查一個子序列是否在 Y 中 —— θ(n)

所以窮舉法在最壞情況下的時間復雜度是 θ(n2m),也就是說花費的時間是指數級的,這簡直太慢了。

2) 動態規劃

首先,我們來看看 LCS 問題是否具有動態規劃問題的兩個特性。

① 最優子結構

C[i,j] = |LCS(x[1...i],y[1...j])|,即C[i,j]表示序列X[1...i]Y[1...j]的最長公共子序列的長度,則 C[m,n] = |LCS(x,y)|就是問題的解。

遞歸推導式:

技術分享

根據上面的遞歸推導式,可以寫出求LCS長度的遞歸偽代碼:

LCS(x,y,i,j)
	if x[i] = y[j]
		then C[i,j] ← LCS(x,y,i-1,j-1)+1
		else C[i,j] ← max{LCS(x,y,i-1,j),LCS(x,y,i,j-1)}
	return C[i,j]

動態規劃就是要解決這個問題,通過用一個表來保存子問題的結果,避免重復的計算,以空間換時間。前面我們已經證明,最長公共子序列問題具有動態規劃所要求的兩個特性,所以 LCS 問題可以用動態規劃來求解。

下面是用動態規劃(打表)解決LCS問題:

技術分享

private static int lcs(char[] x, char[] y, int m, int n) {
		if (m == 0 || n == 0) {
			return 0;
		}
		int[][] c = new int[m + 1][n + 1];
		for (int i = 1; i < m + 1; i++) {
			for (int j = 1; j < n + 1; j++) {
				if (x[i - 1] == y[j - 1]) {
					c[i][j] = c[i - 1][j - 1] + 1;
				} else {
					c[i][j] = Math.max(c[i][j - 1], c[i - 1][j]);
				}
			}
		}
		return c[m][n];
	}

  

動態規劃 最長公共子序列