動態規劃之最長公共子序列求解
關於最長公共子序列(LCS)
最長公共子序列和最長公共子串是有區別的,之前我一直把它們混淆。
- 最長公共子串舉例:假設S1={A,D,C,B,E,X,Q},S2={H,P,D,C,B,E,M,L}
那麼它們的最長公共子串就是{D,C,B,E}。這是我通常理解的東西。
最長公共子序列。
- 最長公共子序列舉例:假設S1={A,B,C,A,D,A,B},S2={B,A,C,D,B,A},那麼它們的LCS就是{B,A,D,B}。
求解最長公共子序列
這是一個動態規劃問題。如何求解最長公共子序列(以下用LCS代替)呢?我們假設已經知道Z={z1,z2,…zk}是X={x1,x2,…,xm}和Y={y1,y2,…,yn}的LCS,那麼可以分以下三種情況討論(具體每種情況證明不再累述):
- xm=yn=zk:那麼Zk-1是Xm-1和Yn-1的LCS。
- xm≠yn,yn≠zk:我們可以把yn去掉,那麼Zk是Xm和Yn-1的LCS。
- xm≠yn,xm≠zk:我們可以把xm去掉,那麼Zk是Xm-1和Yn的LCS。
基於以上情況,我們可以得到LCS遞迴式。我們假設ci表示Xi和Yi的LCS長度,那麼:
- ci=0(i=0或j=0);
- ci=c[i-1]c[j-1]+1(i,j>0且xi=yi);
- ci=max{ci-1,c[i],[j-1]};(i,j>0且xi≠yi)。
這樣我們就可以得到LCS的長度。如何得到具體內容是什麼呢?我們可以借用一個輔助陣列bi,這個陣列用來記錄ci的來源,分別有如下情況:
- ci=ci-1+1,則bi=1;
- ci=ci,則bi=2;
- ci=ci-1,則bi=3。
這樣就可以根據bm反向追蹤LCS,當bi=1,輸出xi;當bi=2,追蹤ci;當bi=3,追蹤ci-1,直到i=0或j=0停止。
演算法設計
(1)初始化。初始化c[][]第1行和第1列為0。
(2)開始操作。具體是將s1[i]分別與s2[j-1](j=1,2,…,len2)進行比較,若字元相等ci=左上角數值+1,且bi=1;若不相等,則ci等於左側或者上側重最大的一個數值,若左側和上側相等,則取左側,且bi=2或3(當取左側為2,取上側為3)。最後的c[][]和b[][]如下所示:
下表是c[][]:
0 | 1 | 2 | 3 | 4 | 5 | 6 | |
---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 | |
A | 0 | 0 | 1 | 1 | 1 | 1 | 1 |
B | 0 | 1 | 1 | 1 | 1 | 2 | 2 |
C | 0 | 1 | 1 | 2 | 2 | 2 | 2 |
A | 0 | 1 | 2 | 2 | 2 | 2 | 3 |
D | 0 | 1 | 2 | 2 | 3 | 3 | 3 |
A | 0 | 1 | 2 | 2 | 3 | 3 | 4 |
B | 0 | 1 | 2 | 2 | 3 | 4 | 4 |
下表是b[][]:
0 | 1 | 2 | 3 | 4 | 5 | 6 | |
---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 | |
1 | 0 | 2 | 1 | 2 | 2 | 2 | 1 |
2 | 0 | 1 | 2 | 2 | 2 | 1 | 2 |
3 | 0 | 3 | 2 | 1 | 2 | 2 | 2 |
4 | 0 | 3 | 1 | 2 | 2 | 2 | 1 |
5 | 0 | 3 | 3 | 2 | 1 | 2 | 2 |
6 | 0 | 3 | 1 | 2 | 3 | 2 | 1 |
7 | 0 | 1 | 3 | 2 | 3 | 1 | 2 |
根據c[][]可以得出,LCS的長度為4(也就是c[][]最後一個值)。然後開始判斷內容是什麼,這是要根據b[][]來。
首先,b7=2,向左找b7=1,所以向左上角找b6,得到字母為s1[6]=[B];
b6=3,向上找b5=1,向左上角找b4,得到字母s1[4]=[D];
b4=2,向左找b4[1],得到字母s1[3]=[A];
b3=3,向上找b2=1,向左上角找b1,得到字母s1[1]=[B].
由於b1=0,所以演算法停止,返回結果為“BADB”。
程式碼演示
void LCSL()
{
int i, j;
for(i=1;i<len1;i++)
for (j = 1; j < len2; j++)
{
if (s1[i - 1] == s2[j - 1])
{
c[i][j] = c[i - 1][j - 1] + 1;
b[i][j] = 1;
}
else
{
if (c[i][j - 1] >= c[i - 1][j])
{
c[i][j] = c[i][j - 1];
b[i][j] = 2;
}
else
{
c[i][j] = c[i - 1][j];
b[i][j] = 3;
}
}
}
}
void print(int i, int j)
{
if (i == 0 || j == 0)
return;
if (b[i][j] == 1)
{
print(i - 1, j - 1);
cout << s1[i - 1];
}
else if (b[i][j] == 2)
print(i, j - 1);
else
print(i - 1, j);
}
相關文章
- 力扣1143. 最長公共子序列 動態規劃之最長公共子序列力扣動態規劃
- [演算法筆記]動態規劃之最長公共子串和最長公共子序列演算法筆記動態規劃
- 動態規劃-最長公共子序列動態規劃
- 動態規劃——最長公共子序列動態規劃
- 動態規劃(最長公共子序列LCS)動態規劃
- 動態規劃求解最長上升子序列問題動態規劃
- 最長公共子序列問題—動態規劃sdut動態規劃
- 動態規劃經典問題----最長公共子序列動態規劃
- 詳解動態規劃最長公共子序列--JavaScript實現動態規劃JavaScript
- 最長公共子序列&迴文字串 nyoj動態規劃字串動態規劃
- 動態規劃:最長上升子序列動態規劃
- 最長上升子序列動態規劃動態規劃
- 動態規劃-最長上升子序列模型動態規劃模型
- 以最長公共子序列問題理解動態規劃演算法(DP)動態規劃演算法
- “最長公共字串子序列”問題的動態規劃法演算法字串動態規劃演算法
- 動態規劃7:最長上升子序列LIS動態規劃
- 淺談最長公共子序列引發的經典動態規劃問題動態規劃
- hud1151 動態規劃 最大的公共子序列動態規劃
- 【LeetCode動態規劃#14】子序列系列題(最長遞增子序列、最長連續遞增序列、最長重複子陣列、最長公共子序列)LeetCode動態規劃陣列
- 動態規劃求最長降序序列動態規劃
- python 動態規劃(揹包問題和最長公共子串)Python動態規劃
- 最長公共子序列
- 動態規劃之最短路徑和動態規劃
- 雙序列動態規劃動態規劃
- 最長公共子序列(JAVA)Java
- [動態規劃] 六、最長迴文子串動態規劃
- 北京大學郭煒-最長上升子序列 動態規劃講解動態規劃
- [LeetCode解題] -- 動態規劃二 [ 子串、子序列問題 ]LeetCode動態規劃
- 動態規劃解最長迴文子序列並優化空間複雜度動態規劃優化複雜度
- 動態規劃之子序列問題動態規劃
- java 實現 最長公共子序列Java
- 最長公共子序列求方案數
- 線性dp:最長公共子序列
- 演算法題:最長公共子序列演算法
- 強化學習(三)用動態規劃(DP)求解強化學習動態規劃
- LeetCode 1626. 無矛盾的最佳球隊---【動態規劃】最長上升子序列變換版-->最大上升子序列和LeetCode動態規劃
- LeetCode 300. 最長上升子序列(Python、動態規劃、貪心演算法)LeetCodePython動態規劃演算法
- LCS 演算法:Javascript 最長公共子序列演算法JavaScript