動態規劃經典問題----最長公共子序列
最長的公共子序列。
例如:X=(A,B,C,B,A,D,B),Y=(B,C,B,A,A,C),那麼最長公共子
序列是 B,C,B,A;
分析:由於如果直接暴力破解,時間複雜度是我們避之不及的爆炸性指數。可以用動態規劃分析其最優子結構,然後建立最優值的遞迴式,有點類似於我們中學所學數列中的通項,但還是有點區別。
(一)假設:已知Zk={z1,z2,z3.......zk}是X={x1,x2,x3,…,xm}和 Y={y1,y2,y3,…,yn}的最優子結構
①:當Xm=Yn=Zk時,Z={z1,z2.....zk-1}時,Zk-1是Xm-1和 Yn-1的最長公共子序列。
反證法:如果Zk-1不是Xm-1和 Yn-1的最長公共子序列。則存在一個序列M為Xm-1和 Yn-1的最長公共子序列,則M>Zk-1,那麼在三序列後面加上Xm=Yn=Zk,因為M+Zk>Zk-1+Zk,則M+Zk為Xm和 Yn的最長公共子序列,這與假設衝突,所以Zk-1就是Xm-1和 Yn-1的最長公共子序列。
②當Xm≠Yn,Xm≠Zk時,我們可以先把Xm去掉,則Zk是Xm-1和Yn的最長公共子序列。
反證法:如果Zk不是Xm-1和Yn的最長公共子序列。則存在一個序列M是Xm-1和Yn的最長公共子序列,所以M>Z,當把Xm加到Xm-1處時,由於M>Z,則M是Xm和Yn的最長公共子序列。這與假設衝突。
③當Xm≠Yn,Yn≠Zk時,同②證法一樣,Zk是Xm和Yn-1的最長公共子序列。
這說明這題具有最優子結構的性質,是典型的D題。
(二) 建立最優值遞迴式
用C[i][j]來儲存最優值
①:當Xm=Yn=Zk時,C[i][j]=C[i1][j-1]+1;
②:當Xm≠Yn時,我們只需要比較Xm-1和Yn與Xm和Yn-1的最長公共子序列哪個更長;即:C[i][j]=max{C[i-1][j],C[i][j-1]};
當i=j=0時,C[i][j]=0;
(三)向上計算最優解
由於最優值只是得到了一個最長的一個數值,並不知道序列是什麼,假設猜C[m][n]=5,我們知道這個最長公共子序列長度是5,那麼這個5是怎樣得到的?我們可以通過倒向追蹤法知道5是從哪裡來的。根據遞迴式:
當Xi=Yj時,C[i][j]=C[i-1][j-1]+1;
當Xi≠Yj時:C[i][j]=max{C[i-1][j],C[i][j-1]};
則C[i][j]有三個來源:C[i-1][j-1]+1和C[i-1][j]和C[i][j-1];
在建立最優值時可以用一個輔助陣列W[i][j]來記錄這三個來源:
C[i][j]=C[i-1][j-1]+1;W[i][j]=1;
C[i][j]=C[i][j-1];W[i][j]=2;
C[i][j]=C[i-1][j]; W[i][j]=3;
這樣就可以根據W[m][n]的值來倒向追蹤,當W[i][j]=1時;輸出Xi,當W[i][j]=2時;追蹤C[i][j-1];當W[i][j]=3時;追蹤C[i-1][j];直到i=0或者j=0;
程式碼細節:
#include<iostream>
#include<string.h>
using namespace std;
#define N 1002
int C[N][N], b[N][N];
char s1[N], s2[N];
int len1, len2;
void LCSL()
{
for(int i=1;i<=len1;i++)
for (int j = 1; j <=len2; j++)
{
if (s1[i-1] == s2[j-1])
{
//如果當前字串相同,則其公共子序列的長度為該字串前的最長公共序列+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)//說明s1[i-1]==s2[j-1]
{
print(i - 1, j - 1);
cout << s1[i - 1];
}
else
if (b[i][j] == 2)//由上面程式可知此時s1[i-1]≠s2[j-1]且最優解來源於C[i][j]=C[i][j-1]
{
print(i, j - 1);//所以遞迴回源點
}
else
print(i - 1, j);
}
int main()
{
cin >> s1;
cin >> s2;
//字串的長度
len1 = strlen(s1);
len2 = strlen(s2);
for (int i= 0; i <= len1; i++)
{
C[i][0] = 0;//初始化第一列為 0
}
for (int j = 0; j <= len2; j++)
{
C[0][j] = 0;//初始化第一行為 0
}
LCSL(); //求解最長公共子序列
cout << "s1 和 s2 的最長公共子序列長度是:" << C[len1][len2] << endl;
cout << "s1 和 s2 的最長公共子序列是:";
print(len1, len2); //遞迴構造最長公共子序列最優解
return 0;
}
相關文章
- 淺談最長公共子序列引發的經典動態規劃問題動態規劃
- 最長公共子序列問題—動態規劃sdut動態規劃
- 動態規劃-最長公共子序列動態規劃
- 動態規劃——最長公共子序列動態規劃
- 動態規劃(最長公共子序列LCS)動態規劃
- 力扣1143. 最長公共子序列 動態規劃之最長公共子序列力扣動態規劃
- 以最長公共子序列問題理解動態規劃演算法(DP)動態規劃演算法
- 動態規劃求解最長上升子序列問題動態規劃
- 動態規劃之最長公共子序列求解動態規劃
- 詳解動態規劃最長公共子序列--JavaScript實現動態規劃JavaScript
- python 動態規劃(揹包問題和最長公共子串)Python動態規劃
- 【LeetCode動態規劃#14】子序列系列題(最長遞增子序列、最長連續遞增序列、最長重複子陣列、最長公共子序列)LeetCode動態規劃陣列
- 動態規劃:最長上升子序列動態規劃
- 最長上升子序列動態規劃動態規劃
- 動態規劃-最長上升子序列模型動態規劃模型
- 經典演算法題每日演練——最長公共子序列演算法
- [LeetCode解題] -- 動態規劃二 [ 子串、子序列問題 ]LeetCode動態規劃
- 動態規劃求最長降序序列動態規劃
- 最長公共子序列
- 動態規劃之子序列問題動態規劃
- 最長公共子序列(JAVA)Java
- [動態規劃] 六、最長迴文子串動態規劃
- 北京大學郭煒-最長上升子序列 動態規劃講解動態規劃
- [題解]P1439 【模板】最長公共子序列
- 最長公共子序列求方案數
- java 實現 最長公共子序列Java
- 最長公共子序列 Longest Common Subsequence
- 線性dp:最長公共子序列
- LeetCode 1143.最長公共子序列LeetCode
- LeetCode 300. 最長上升子序列(Python、動態規劃、貪心演算法)LeetCodePython動態規劃演算法
- 領釦LintCode演算法問題答案-77. 最長公共子序列演算法
- LeetCode 1626. 無矛盾的最佳球隊---【動態規劃】最長上升子序列變換版-->最大上升子序列和LeetCode動態規劃
- 最長公共子序列LCS 輸出所有LCS
- 51Nod 1006 最長公共子序列Lcs
- 最長公共子序列你學會了嗎
- 雙序列動態規劃動態規劃
- 字串篇(python)—兩個字串的最長公共子序列字串Python
- 【動態規劃】揹包問題動態規劃