算法導論-動態規划(最長公共子序列問題LCS)-C++實現


    首先定義一個給定序列的子序列,就是將給定序列中零個或多個元素去掉之后得到的結果,其形式化定義如下:給定一個序列X = <x1,x,..., xm>,另一個序列Z =<z1,z,..., zk> 滿足如下條件時稱為X的子序列,即存在一個嚴格遞增的X的下標序列<i1,i,..., 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的公共子序列。

   最長公共子序列問題(longest-common-subsequence problem)可用動態規划方法高效地求解。

   步驟1:刻畫最長公共子序列的特征

   LCS問題具有 最優子結構性質。子問題的自然分類對應兩個輸入序列的“前綴"對。"前綴"的定義如下:給定一個序列X = <x1,x,..., xm>,對於i = 0,1,...,m,定義X的第i前綴為Xi = <x1,x,..., xi>。例如,若 X = <A,B,C,B,D,A,B>,則 X4 = <A,B,C,B>,X0為空串。

    令X = <x1,x,..., xm>和Y = <y1,y,..., yn> 為兩個序列,Z =<z1,z,..., zk>為X和Y的任意LCS。

  1.     如果Xm = Yn,則 Zk =  Xm = Yn且Zk-1 是Xm-1和Yn-1的一個LCS。
  2.     如果 Xm ≠ Yn,那么Zk ≠  Xm意味着Z是Xm-1和Y的一個LCS。
  3.     如果 Xm ≠ Yn,那么Zk ≠  Yn意味着Z是X和Yn-1的一個LCS。

    步驟2:一個遞歸解

    很容易看出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問題的最優子結構性質,可得如下公式:

     步驟3:計算LCS的長度

    過程LCS-LENGTH接受兩個序列X = <x1,x,..., xm>和Y = <y1,y,..., 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] = "\"

    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 }

輸出:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM