首先定義一個給定序列的子序列,就是將給定序列中零個或多個元素去掉以後獲得的結果,其形式化定義以下:給定一個序列X = <x1,x2 ,..., xm>,另外一個序列Z =<z1,z2 ,..., zk> 知足以下條件時稱爲X的子序列,即存在一個嚴格遞增的X的下標序列<i1,i2 ,..., ik>,對於全部j = 1,2,...,k,知足xij = zj,例如,Z=<B,C,D,B>是X=<A,B,C,B,D,A,B>的子序列,對應的下標序列爲<2,3,5,7>。給定兩個序列X和Y,若是Z是X的子序列,也是Y的子序列,則稱它是X和Y的公共子序列。spa
最長公共子序列問題(longest-common-subsequence problem)可用動態規劃方法高效地求解。3d
步驟1:刻畫最長公共子序列的特徵code
LCS問題具備 最優子結構性質。子問題的天然分類對應兩個輸入序列的「前綴"對。"前綴"的定義以下:給定一個序列X = <x1,x2 ,..., xm>,對於i = 0,1,...,m,定義X的第i前綴爲Xi = <x1,x2 ,..., xi>。例如,若 X = <A,B,C,B,D,A,B>,則 X4 = <A,B,C,B>,X0爲空串。blog
令X = <x1,x2 ,..., xm>和Y = <y1,y2 ,..., yn> 爲兩個序列,Z =<z1,z2 ,..., zk>爲X和Y的任意LCS。遞歸
步驟2:一個遞歸解string
很容易看出LCS問題的重疊子問題性質。爲了求X和Y的一個LCS,咱們可能須要求X和Yn-1的一個LCS及Xm-1和Y的一個LCS。可是這幾個子問題都包含求解Xm-1和Yn-1的LCS的子子問題。咱們定義c[i,j]表示Xi和Yj的LCS的長度。若是i= 0 或j = 0,即一個序列長度爲0,那麼LCS的長度爲0,根據LCS問題的最優子結構性質,可得以下公式:table
步驟3:計算LCS的長度class
過程LCS-LENGTH接受兩個序列X = <x1,x2 ,..., xm>和Y = <y1,y2 ,..., yn>爲輸入。它將c[i,j]的值保存在表c[0...m,0...n]中,過程還維護一個表b[1...m,1...n],幫助構造最優解。b[i,j]指向的表項對應計算c[i,j]時所選擇的子問題最優解。過程返回表b和表c,c[m,n]保存了X和Y的長度。方法
//僞代碼
LCS-LENGTH(X,Y)
m = X.length
n = Y.length
let b[1..m,1..n] and c[0..m,0..n]be new tables
for i = 1 to m
c[i,0] = 0
for j = 0 to n
c[0,j] = 0
for i = 1 to m
for j = 1 to n
if xi == yi
c[i,j] = c[i-1,j-1] + 1
b[i,j] = "\"im
else if c[i-1,j] ≥ c[i,j-1]
c[i,j] = c[i-1,j]
b[i,j] = "|"
else
c[i,j] = c[i,j-1]
b[i,j] = "—"
return c and b
下圖顯示了LCS-LENGTH對輸入序列X= <A,B,C,B,D,A,B>和Y=<B,D,C,A,B,A>生成的結果。過程的運行時間爲O(mn)。
步驟4:構造LCS
利用LCS-LENGTH返回的表b快速構造X和Y的LCS,只需簡單地從b[m,n]開始,並按箭頭方向追蹤下去便可。擋在表項b[i,j]中遇到一個」\"時,意味着xi=yi是LCS的一個元素。下面的遞歸過程會按正確的順序打印出X和Y的一個LCS。對它的起始調用爲PRINT-LCS(b,X,X.length,Y.length)。
PRINT-LCS(b,X,i,j)
if == 0 or j==0
return
if b[i,j] == "\"
PRINT-LCS(b,X,i-1,j-1)
print xi
else if b[i,j] == "|"
PRINT-LCS(b,X,i-1,j)
else
PRINT-LCS(b,X,i,j-1)
實現:
1 void lcsLength(string x,string y, vector< vector<int>> &c, vector< vector<char>> &b) 2 { 3 int m = x.size(); 4 int n = y.size(); 5 c.resize(m+1); 6 b.resize(m+1); 7 for(int i = 0; i < c.size(); ++i) 8 c[i].resize(n+1); 9 for(int i = 0; i < b.size(); ++i) 10 b[i].resize(n+1); 11 12 for(int i = 1; i <= m; ++i){ 13 for(int j = 1; j <= n; ++j){ 14 if(x[i-1] == y[j-1]){ 15 c[i][j] = c[i-1][j-1]+1; 16 b[i][j] = 'c'; 17 }else if(c[i-1][j] >= c[i][j-1]){ 18 c[i][j] = c[i-1][j]; 19 b[i][j] ='u'; 20 }else{ 21 c[i][j] = c[i][j-1]; 22 b[i][j] = 'l'; 23 } 24 } 25 } 26 }
1 void print_lcs(vector< vector<char>> &b,string x, int i, int j) 2 { 3 if(i == 0 || j == 0) 4 return; 5 if(b[i][j] == 'c'){ 6 print_lcs(b,x,i-1,j-1); 7 cout << x[i-1]; 8 }else if(b[i][j] == 'u') 9 print_lcs(b,x,i-1,j); 10 else 11 print_lcs(b,x,i,j-1); 12 }
例子:
1 int main() 2 { 3 string x = "ABCBDAB"; 4 string y = "BDCABA"; 5 vector< vector<int>> c; 6 vector< vector<char>> b; 7 8 lcsLength(x,y,c,b); 9 print_lcs(b,x,x.size(),y.size()); 10 }
輸出: