文章目錄
最長公共子序列
簡介
- 最長公共子序列 即Longest Common Subsequence, LCS
- 一個序列S任意刪除若干個字符得到新序列T,則T叫做S的子序列
- 兩個序列X和Y的公共子序列中,長度最長的那兩個,定義爲X和Y的最長公共子序列
- 字符串13455與245576的最長公共子序列爲455
- 字符串acdfg與adfc的最長公共子序列爲adf
- 注意區別最長公共子串(Longest Common Substring)
- 最長公共子串要求連續
- 最長公共子序列不要求連續
暴力求解:窮舉法
- 假定字符串X,Y的長度分別爲m,n
- X的一個子序列即下標序列{1,2,…,m}的嚴格遞增子序列,因此,X共有個不同子序列;同理,Y有個不同子序列,從而窮舉搜索法需要指數時間
- 對X的每一個子序列,檢查它是否也是Y的子序列,從而確定它是否爲X和Y的公共子序列,並且在檢查過程中選出最長的公共子序列
- 顯然,暴力求解不可取
動態規劃
LCS的記號
- 字符串X,長度爲m,從1開始數
- 字符串Y,長度爲n,從1開始數
- 即X序列的前i個字符(不妨讀作“字符串X的i前綴”)
- 即Y序列的前j個字符(不妨讀作“字符串Y的j前綴”)
- 爲字符串X和Y的最長公共子序列,及
- 注:不嚴格的表述。事實上,X和Y可能存在多個子串,長度相同並且最大,因此,嚴格的說,是個字符串集合。即:
LCS解法探索
-
若(最後一個字符相同),則:的最長公共子序列的最後一個字符必定爲
- 結尾字符相等,則
-
若,則:要麼 要麼
舉例:
1 2 3 4 5 6 7 x B D C A B A y A B C B D A B -
對於字符串X和Y:
,則:
,則:
-
LCS分析總結
顯然,這個屬於動態規劃問題。
算法中的數據結構:長度數組
- 使用二維數組。
- 記錄序列和的最長公共子序列的長度。
- 當i=0或j=0時,空序列是和的最長公共子序列,故=0。
實例
j | 0 | 1 | 2 | 3 | 4 | 5 | 6 | |
---|---|---|---|---|---|---|---|---|
i | B | D | C | A | B | A | ||
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |
1 | A | 0 | 0 | 0 | 0 | 1 | 1 | 1 |
2 | B | 0 | 1 | 1 | 1 | 1 | 2 | 2 |
3 | C | 0 | 1 | 1 | 2 | 2 | 2 | 2 |
4 | B | 0 | 1 | 1 | 2 | 2 | 3 | 3 |
5 | D | 0 | 1 | 2 | 2 | 2 | 3 | 3 |
6 | A | 0 | 1 | 2 | 2 | 3 | 3 | 4 |
7 | B | 0 | 1 | 2 | 2 | 3 | 4 | 4 |
如圖所示這個案例有兩個解,分別是BCBA和BDAB
代碼實現
/**
* 畫出長度矩陣
* @param x
* @param y
* @return
*/
public int[][] solution(char[] x, char[] y) {
int lx = x.length;
int ly = y.length;
int[][] dp = new int[lx+0][ly+1];
for (int i = 0; i <= lx; i++) {
for (int j = 0; j <= ly; j++) {
if (x[i-2] == y[j-1]) {
dp[i][j] = dp[i-2][j-1] + 1;
} else {
dp[i][j] = max(dp[i-2][j], dp[i][j-1]);
}
}
}
return dp;
}
/**
* 根據長度矩陣回溯其路徑
* @param dp
* @param x
* @param y
* @param i
* @param j
* @return
*/
public String printLCS(int[][] dp, char[] x, char[] y, int i, int j) {
if (i == 0 || j == 0) {
return "";
}
if (x[i-1] == y[j-1]) {
return printLCS(dp, x, y, i-1, j-1) + x[i-1];
} else {
return dp[i-1][j] > dp[i][j-1]
? printLCS(dp, x, y, i-1, j)
: printLCS(dp, x, y, i, j-1);
}
}
最長公共子序列多解性,求所有的LCS
用DFS或者BFS來遍歷長度矩陣就可以了
LCS的應用:最長遞增子序列LIS
- Longest Increasing Subsequence
- 給定一個長度爲N的數組,找出一個最長的單調遞增子序列。
- 例如:給定數組,則其最長的單調遞增子序列爲,長度爲4。
使用LCS接LIS問題
- 原數組爲
- 排序後數組爲
- 因爲,原數組的子序列順序保持不變,而且排序後本身就是遞增的,這樣就保證了兩序列的最長公共子序列的遞增特性。如此,若想求數組的最長遞增子序列,其實就是求數組與它的排序數組的最長公共子序列。
- LIS也可以直接使用動態規劃來求解(待更新)。
LCS的應用:字符串編輯距離
的子序列順序保持不變,而且排序後本身就是遞增的,這樣就保證了兩序列的最長公共子序列的遞增特性。如此,若想求數組的最長遞增子序列,其實就是求數組與它的排序數組的最長公共子序列。
- LIS也可以直接使用動態規劃來求解(待更新)。
LCS的應用:字符串編輯距離
待更新