最長公共子序列

最長公共子序列

簡介

  • 最長公共子序列 即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共有2m2^m個不同子序列;同理,Y有2n2^n個不同子序列,從而窮舉搜索法需要指數時間O(2m2n)O(2^m*2^n)
  • 對X的每一個子序列,檢查它是否也是Y的子序列,從而確定它是否爲X和Y的公共子序列,並且在檢查過程中選出最長的公共子序列
  • 顯然,暴力求解不可取

動態規劃

LCS的記號

  • 字符串X,長度爲m,從1開始數
  • 字符串Y,長度爲n,從1開始數
  • Xi=x1,,xiX_i = \lang x_1,\cdots, x_i\rang 即X序列的前i個字符(1im)(1 \le i \le m)XiX_i不妨讀作“字符串X的i前綴”)
  • Yj=y1,,yjY_j = \lang y_1,\cdots, y_j\rang 即Y序列的前j個字符(1jn)(1 \le j \le n)YjY_j不妨讀作“字符串Y的j前綴”)
  • LCS(X,Y)LCS(X,Y)爲字符串X和Y的最長公共子序列,及Z=zi,,zkZ=\lang z_i , \cdots , z_k\rang
    • 注:不嚴格的表述。事實上,X和Y可能存在多個子串,長度相同並且最大,因此,LCS(X,Y)LCS(X,Y)嚴格的說,是個字符串集合。即:ZLCS(X,Y)Z\in LCS(X,Y)

LCS解法探索

  1. xm=ynx_m = y_n

    xm=ynx_m = y_n(最後一個字符相同),則:Xm=YnX_m = Y_n的最長公共子序列ZkZ_k的最後一個字符必定爲xm(=yn)x_m(=y_n)

    • zk=xm=ynz_k = x_m = y_n
    • 結尾字符相等,則LCS(Xm,Yn)=LCS(Xm1,Yn1)+xmLCS(X_m,Y_n) = LCS(X_{m-1},Y_{n-1}) + x_m
  2. xmynx_m \neq y_n

    xmynx_m \neq y_n,則:要麼LCS(Xm,Yn)=LCS(Xm1,Yn)LCS(X_m,Y_n) = LCS(X_{m-1},Y_n) 要麼LCS(Xm,Yn)=LCS(Xm,Yn1)LCS(X_m,Y_n) = LCS(X_m,Y_{n-1})

    LCS(Xm,Yn)=max{LCS(Xm1,Yn),LCS(Xm,Yn1)}LCS(X_m,Y_n) = max \lbrace LCS(X_{m-1},Y_n) , LCS(X_m,Y_{n-1}) \rbrace

    舉例:

    1 2 3 4 5 6 7
    x B D C A B A
    y A B C B D A B
    • 對於字符串X和Y:

      x2y2x_2 \neq y_2,則:LCS(BD,AB)=max{LCS(BD,A),LCS(B,AB)}LCS(B{\color{red}{D}},A{\color{blue}{B}}) = max \lbrace LCS(B{\color{red}{D}}, A), LCS(B, A{\color{blue}{B}}) \rbrace

      x4y5x_4 \neq y_5,則:LCS(BDCA,ABCBD)=max{LCS(BDCA,ABCB),LCS(BDC,ABCBD)}LCS(BDC{\color{green}{A}},ABCB{\color{purple}{D}}) = max \lbrace LCS(BDC{\color{green}{A}}, ABCB), LCS(BDC, ABCB{\color{purple}{D}}) \rbrace

LCS分析總結

LCS(Xm,Yn)={LCS(Xm,Yn)=LCS(Xm1,Yn1)+xmxm=ynLCS(Xm,Yn)=max{LCS(Xm1,Yn),LCS(Xm,Yn1)}xmyn LCS(X_m,Y_n)= \begin{cases} LCS(X_m,Y_n) = LCS(X_{m-1},Y_{n-1}) + x_m \qquad x_m = y_n \\ LCS(X_m,Y_n) = max \lbrace LCS(X_{m-1},Y_n) , LCS(X_m,Y_{n-1}) \rbrace \qquad x_m \neq y_n \end{cases}

顯然,這個屬於動態規劃問題。

算法中的數據結構:長度數組

  1. 使用二維數組C[m,n]C[m,n]
  2. c[i,j]c[i,j]記錄序列XiX_iYjY_j的最長公共子序列的長度。
    • 當i=0或j=0時,空序列是XiX_iYjY_j的最長公共子序列,故c[i,j]c[i,j]=0。

c(i,j)={0i=0j=0c(i1,j1)+1i>0,j>0,xi=yimax{c(i1,j),c(i,j1)}i>0,j>0,xiyj c(i,j) = \begin{cases} 0 \qquad i=0 || j = 0 \\ c(i-1,j-1)+1 \qquad i\gt0,j\gt0,x_i = y_i \\ max\lbrace c(i-1,j),c(i,j-1)\rbrace \qquad i\gt0,j\gt0,x_i \neq y_j \end{cases}

實例

X=A,B,C,B,D,A,BX = \lang A,B,C,B,D,A,B \rang

Y=B,D,C,A,B,AY=\lang B,D,C,A,B,A \rang

j 0 1 2 3 4 5 6
i yjy_j B D C A B A
0 xix_i 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

如圖所示這個案例有兩個解,分別是BCBABDAB

代碼實現

/**
 * 畫出長度矩陣
 * @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的數組,找出一個最長的單調遞增子序列。
  • 例如:給定數組{5,6,7,1,2,8}\lbrace 5,6,7,1,2,8\rbrace,則其最長的單調遞增子序列爲{5,6,7,8}\lbrace 5,6,7,8 \rbrace,長度爲4。

使用LCS接LIS問題

  • 原數組爲A1{5,6,7,1,2,8}A_1\lbrace 5,6,7,1,2,8\rbrace
  • 排序後數組爲 A2{1,2,5,6,7,8}A_2\lbrace 1,2,5,6,7,8\rbrace
  • 因爲,原數組A1A_1的子序列順序保持不變,而且排序後A2A_2本身就是遞增的,這樣就保證了兩序列的最長公共子序列的遞增特性。如此,若想求數組A1A_1的最長遞增子序列,其實就是求數組A1A_1與它的排序數組A2A_2的最長公共子序列。
  • LIS也可以直接使用動態規劃來求解(待更新)。

LCS的應用:字符串編輯距離

A1A_1的子序列順序保持不變,而且排序後A2A_2本身就是遞增的,這樣就保證了兩序列的最長公共子序列的遞增特性。如此,若想求數組A1A_1的最長遞增子序列,其實就是求數組A1A_1與它的排序數組A2A_2的最長公共子序列。

  • LIS也可以直接使用動態規劃來求解(待更新)。

LCS的應用:字符串編輯距離

待更新

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章