最长公共子序列

文章目录

  • 最长公共子序列
    • 简介
    • 暴力求解:穷举法
    • 动态规划
      • LCS的记号
      • LCS解法探索
      • LCS分析总结
      • 算法中的数据结构:长度数组
      • 实例
      • 代码实现
      • 最长公共子序列多解性,求所有的LCS
    • LCS的应用:最长递增子序列LIS
      • 使用LCS接LIS问题
    • LCS的应用:字符串编辑距离
    • LCS的应用:字符串编辑距离

最长公共子序列

简介

  • 最长公共子序列 即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共有 2 m 2^m 2m个不同子序列;同理,Y有 2 n 2^n 2n个不同子序列,从而穷举搜索法需要指数时间 O ( 2 m ∗ 2 n ) O(2^m*2^n) O(2m2n)
  • 对X的每一个子序列,检查它是否也是Y的子序列,从而确定它是否为X和Y的公共子序列,并且在检查过程中选出最长的公共子序列
  • 显然,暴力求解不可取

动态规划

LCS的记号

  • 字符串X,长度为m,从1开始数
  • 字符串Y,长度为n,从1开始数
  • X i = ⟨ x 1 , ⋯   , x i ⟩ X_i = \lang x_1,\cdots, x_i\rang Xi=x1,,xi 即X序列的前i个字符 ( 1 ≤ i ≤ m ) (1 \le i \le m) (1im) X i X_i Xi不妨读作“字符串X的i前缀”)
  • Y j = ⟨ y 1 , ⋯   , y j ⟩ Y_j = \lang y_1,\cdots, y_j\rang Yj=y1,,yj 即Y序列的前j个字符 ( 1 ≤ j ≤ n ) (1 \le j \le n) (1jn) Y j Y_j Yj不妨读作“字符串Y的j前缀”)
  • L C S ( X , Y ) LCS(X,Y) LCS(X,Y)为字符串X和Y的最长公共子序列,及 Z = ⟨ z i , ⋯   , z k ⟩ Z=\lang z_i , \cdots , z_k\rang Z=zi,,zk
    • 注:不严格的表述。事实上,X和Y可能存在多个子串,长度相同并且最大,因此, L C S ( X , Y ) LCS(X,Y) LCS(X,Y)严格的说,是个字符串集合。即: Z ∈ L C S ( X , Y ) Z\in LCS(X,Y) ZLCS(X,Y)

LCS解法探索

  1. x m = y n x_m = y_n xm=yn

    x m = y n x_m = y_n xm=yn(最后一个字符相同),则: X m = Y n X_m = Y_n Xm=Yn的最长公共子序列 Z k Z_k Zk的最后一个字符必定为 x m ( = y n ) x_m(=y_n) xm(=yn)

    • z k = x m = y n z_k = x_m = y_n zk=xm=yn
    • 结尾字符相等,则 L C S ( X m , Y n ) = L C S ( X m − 1 , Y n − 1 ) + x m LCS(X_m,Y_n) = LCS(X_{m-1},Y_{n-1}) + x_m LCS(Xm,Yn)=LCS(Xm1,Yn1)+xm
  2. x m ≠ y n x_m \neq y_n xm=yn

    x m ≠ y n x_m \neq y_n xm=yn,则:要么 L C S ( X m , Y n ) = L C S ( X m − 1 , Y n ) LCS(X_m,Y_n) = LCS(X_{m-1},Y_n) LCS(Xm,Yn)=LCS(Xm1,Yn) 要么 L C S ( X m , Y n ) = L C S ( X m , Y n − 1 ) LCS(X_m,Y_n) = LCS(X_m,Y_{n-1}) LCS(Xm,Yn)=LCS(Xm,Yn1)

    L C S ( X m , Y n ) = m a x { L C S ( X m − 1 , Y n ) , L C S ( X m , Y n − 1 ) } LCS(X_m,Y_n) = max \lbrace LCS(X_{m-1},Y_n) , LCS(X_m,Y_{n-1}) \rbrace LCS(Xm,Yn)=max{LCS(Xm1,Yn),LCS(Xm,Yn1)}

    举例:

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

      x 2 ≠ y 2 x_2 \neq y_2 x2=y2,则: L C S ( B D , A B ) = m a x { L C S ( B D , A ) , L C S ( B , A B ) } LCS(B{\color{red}{D}},A{\color{blue}{B}}) = max \lbrace LCS(B{\color{red}{D}}, A), LCS(B, A{\color{blue}{B}}) \rbrace LCS(BD,AB)=max{LCS(BD,A),LCS(B,AB)}

      x 4 ≠ y 5 x_4 \neq y_5 x4=y5,则: L C S ( B D C A , A B C B D ) = m a x { L C S ( B D C A , A B C B ) , L C S ( B D C , A B C B D ) } 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(BDCA,ABCBD)=max{LCS(BDCA,ABCB),LCS(BDC,ABCBD)}

LCS分析总结

L C S ( X m , Y n ) = { L C S ( X m , Y n ) = L C S ( X m − 1 , Y n − 1 ) + x m x m = y n L C S ( X m , Y n ) = m a x { L C S ( X m − 1 , Y n ) , L C S ( X m , Y n − 1 ) } x m ≠ y n 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} LCS(Xm,Yn)={LCS(Xm,Yn)=LCS(Xm1,Yn1)+xmxm=ynLCS(Xm,Yn)=max{LCS(Xm1,Yn),LCS(Xm,Yn1)}xm=yn

显然,这个属于动态规划问题。

算法中的数据结构:长度数组

  1. 使用二维数组 C [ m , n ] C[m,n] C[m,n]
  2. c [ i , j ] c[i,j] c[i,j]记录序列 X i X_i Xi Y j Y_j Yj的最长公共子序列的长度。
    • 当i=0或j=0时,空序列是 X i X_i Xi Y j Y_j Yj的最长公共子序列,故 c [ i , j ] c[i,j] c[i,j]=0。

c ( i , j ) = { 0 i = 0 ∣ ∣ j = 0 c ( i − 1 , j − 1 ) + 1 i > 0 , j > 0 , x i = y i m a x { c ( i − 1 , j ) , c ( i , j − 1 ) } i > 0 , j > 0 , x i ≠ y j 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} c(i,j)=0i=0j=0c(i1,j1)+1i>0,j>0,xi=yimax{c(i1,j),c(i,j1)}i>0,j>0,xi=yj

实例

X = ⟨ A , B , C , B , D , A , B ⟩ X = \lang A,B,C,B,D,A,B \rang X=A,B,C,B,D,A,B

Y = ⟨ B , D , C , A , B , A ⟩ Y=\lang B,D,C,A,B,A \rang Y=B,D,C,A,B,A

j 0 1 2 3 4 5 6
i y j y_j yj B D C A B A
0 x i x_i xi 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,1,2,8},则其最长的单调递增子序列为 { 5 , 6 , 7 , 8 } \lbrace 5,6,7,8 \rbrace {5,6,7,8},长度为4。

使用LCS接LIS问题

  • 原数组为 A 1 { 5 , 6 , 7 , 1 , 2 , 8 } A_1\lbrace 5,6,7,1,2,8\rbrace A1{5,6,7,1,2,8}
  • 排序后数组为 A 2 { 1 , 2 , 5 , 6 , 7 , 8 } A_2\lbrace 1,2,5,6,7,8\rbrace A2{1,2,5,6,7,8}
  • 因为,原数组 A 1 A_1 A1的子序列顺序保持不变,而且排序后 A 2 A_2 A2本身就是递增的,这样就保证了两序列的最长公共子序列的递增特性。如此,若想求数组 A 1 A_1 A1的最长递增子序列,其实就是求数组 A 1 A_1 A1与它的排序数组 A 2 A_2 A2的最长公共子序列。
  • LIS也可以直接使用动态规划来求解(待更新)。

LCS的应用:字符串编辑距离

A 1 A_1 A1的子序列顺序保持不变,而且排序后 A 2 A_2 A2本身就是递增的,这样就保证了两序列的最长公共子序列的递增特性。如此,若想求数组 A 1 A_1 A1的最长递增子序列,其实就是求数组 A 1 A_1 A1与它的排序数组 A 2 A_2 A2的最长公共子序列。

  • LIS也可以直接使用动态规划来求解(待更新)。

LCS的应用:字符串编辑距离

待更新

你可能感兴趣的:(算法)