最长公共子序列问题(Longest common subsequence,LCS)

参考屈婉玲的youtube视频:https://www.youtube.com/watch?v=zkCHuCCoQEY

参考:《算法导论》P390页15.4 Longest common subsequence

问题描述:序列X={x1,x2,…,xn},Y={y1,y2,…,yn},当Z={z1,z2…,zn}是X的严格递增下标顺序(可以不连续)的子集,也是Y的严格递增下标顺序(可以不连续)的子集,则Z是X和Y的公共子序列。例如X={A,B,C,B,D,A,B},Y={B,D,C,A,B,A},{B,C,A}、{B,C,B,A}、{B,D,A,B}都是X和Y的公共子序列。其中最长的公共子序列叫做Longest common subsequence,即经典的LCS。

  具体点:char[]xArray和char[] yArray是字符数组,长度分别为m、n,求他们的LCS

【分析】自顶向下分析,二维数组cTable[i][j]记录xArray[0~i],yArray[0~j]的最长公共子序列的长度,则cTable[m][n]记录xArray[0~m],yArray[0~n]的最长公共子序列的长度

1)   如果xArray[m]=yArray[n],表明最后一个元素xArray[m]是LCS中的元素,xArray[0~m],yArray[0~n]的最长公共子序列=xArray[0~m-1],yArray[0~n-1]的最长公共子序列+1,即cTable[m][n]=cTable[m-1][n-1]。

2)   如果xArray[m]!=yArray[n],表明xArray[m]、yArray[n]都有可能是LCS中的元素,但不能同时是。如果xArray[m]可能是,则xArray[0~m],yArray[0~n]的最长公共子序列=xArray[0~m],yArray[0~n-1]的最长公共子序列;如果yArray[n]可能是,则xArray[0~m],yArray[0~n]的最长公共子序列=xArray[0~m-1],yArray[0~n]的最长公共子序列。即cTable[m][n]=max(cTable[m-1][n], cTable[m][n-1])。

状态递归方程为:


参考《算法导论》P394页伪代码,java实现如下:

[java]  view plain  copy
  1. /**   
  2.      * 创建时间:2014年9月3日 下午9:00:13   
  3.      * 项目名称:Test   
  4.      * @author Cao Yanfeng  Peking University 
  5.      * @since JDK 1.6.0_21   
  6.      * 类说明:  最长公共子序列问题(Longest common subsequence,LCS) 
  7.      */  
  8.     public static void main(String[] args) {  
  9.         // TODO 自动生成的方法存根  
  10.         String x="ABCBDABCBACABC";  
  11.         String y="BDCABACABABCB";  
  12.         int temp=getLCSLength(x, y);  
  13.         System.out.println("\n长度为:"+temp);  
  14.     }  
  15.     public static int getLCSLength(String x,String y) {  
  16.         char[] xArray=x.toCharArray();  
  17.         char[] yArray=y.toCharArray();  
  18.         int m=xArray.length;  
  19.         int n=yArray.length;  
  20.         int [][] bTable=new int[m][n];  
  21.         /*cTable[i][j]记录xArray[0~i],yArray[0~j]的最长公共子序列的长度*/  
  22.         int [][] cTable=new int[m+1][n+1];  
  23.         for (int i = 0; i <m; i++) {  
  24.             for (int j = 0; j < n; j++) {  
  25.                 if (xArray[i]==yArray[j]) {  
  26.                     cTable[i+1][j+1]=cTable[i][j]+1;  
  27.                     bTable[i][j]=2;//相等标记为2  
  28.                 }else if (cTable[i][j+1]>=cTable[i+1][j]) {  
  29.                     cTable[i+1][j+1]=cTable[i][j+1];  
  30.                     bTable[i][j]=1;  
  31.                 }else {  
  32.                     cTable[i+1][j+1]=cTable[i+1][j];  
  33.                     bTable[i][j]=3;  
  34.                 }  
  35.                   
  36.             }  
  37.               
  38.         }  
  39.         System.out.print("最大子数组为:");  
  40.         printLCS(xArray, bTable, m-1,n-1);  
  41.         return cTable[m][n];  
  42.     }  
  43.     /*输出最佳路径即最长公共子序列*/  
  44.     public static void printLCS (char[] xArray,int[][] bTable,int i,int j) {  
  45.         if (i==-1||j==-1) {  
  46.             return;  
  47.         }  
  48.         if (bTable[i][j]==2) {  
  49.             printLCS(xArray, bTable, i-1, j-1);  
  50.             System.out.print(xArray[i]);  
  51.         }else if (bTable[i][j]==1) {  
  52.             printLCS(xArray, bTable, i-1, j);  
  53.         }else {  
  54.             printLCS(xArray, bTable, i, j-1);  
  55.         }  
  56.     }  
  57.   
  58. }  

*****************************************

控制台输出:

最大子数组为:BCBACBACB

长度为:9

--------------------------------------------------------------------------------------------------------

或者以下写法

class Untitled {

	  public static void main(String[] args) {  
        // TODO 自动生成的方法存根  
        String x="ABCBDABCBACABC";  
        String y="BDCABACABABCB";  
        int temp=getLCSLength(x, y);  
        System.out.println("\n长度为:"+temp);  
    }  
    public static int getLCSLength(String x,String y) {  
        char[] xArray=x.toCharArray();  
        char[] yArray=y.toCharArray();  
        int m=xArray.length;  
        int n=yArray.length;  
        int [][] bTable=new int[m][n];  
        /*cTable[i][j]记录xArray[0~i],yArray[0~j]的最长公共子序列的长度*/  
        int [][] cTable=new int[m+1][n+1];  
        for (int i = 0; i <=m; i++) {  
            for (int j = 0; j <=n; j++) {  
				if(i==0||j==0){
				   cTable[i][j]=0;
				}else{
                if (xArray[i-1]==yArray[j-1]) {  
                    cTable[i][j]=cTable[i-1][j-1]+1;  
                    bTable[i-1][j-1]=2;//相等标记为2  
                }else if (cTable[i-1][j]>=cTable[i][j-1]) {  
                    cTable[i][j]=cTable[i-1][j];  
                    bTable[i-1][j-1]=1;  
                }else {  
                    cTable[i][j]=cTable[i][j-1];  
                    bTable[i-1][j-1]=3;  
                }  
				}
                  
            }  
              
        }  
        System.out.print("最大子数组为:");  
        printLCS(xArray, bTable, m-1,n-1);  
        return cTable[m][n];  
    }  
    /*输出最佳路径即最长公共子序列*/  
    public static void printLCS (char[] xArray,int[][] bTable,int i,int j) {  
        if (i==-1||j==-1) {  
            return;  
        }  
        if (bTable[i][j]==2) {  
            printLCS(xArray, bTable, i-1, j-1);  
            System.out.print(xArray[i]);  
        }else if (bTable[i][j]==1) {  
            printLCS(xArray, bTable, i-1, j);  
        }else {  
            printLCS(xArray, bTable, i, j-1);  
        }  
    }  
  
}  
	

通过java在线运行环境http://run.jser.com/java.html

执行结果

最大子数组为:BCBACBACB
长度为:9
执行时间:2.04秒


如果只要求最长公共子序列的长度应该怎么求呢?

参考http://www.cnblogs.com/yuzhangcmu/p/4199531.html
cTable[m][n]记录xArray[0~m],yArray[0~n]的最长公共子序列的长度

public class Solution {
    /**
     * @param A, B: Two strings.
     * @return: The length of longest common subsequence of A and B.
     */
    public int longestCommonSubsequence(String A, String B) {
        // write your code here
        if (A == null || B == null) {
            return 0;
        }
        
        int lenA = A.length();
        int lenB = B.length();
        int[][] D = new int[lenA + 1][lenB + 1];
        
        for (int i = 0; i <= lenA; i++) {
            for (int j = 0; j <= lenB; j++) {
                if (i == 0 || j == 0) {
                    D[i][j] = 0;
                } else {
                    if (A.charAt(i - 1) == B.charAt(j - 1)) {
                        D[i][j] = D[i - 1][j - 1] + 1;
                    } else {
                        D[i][j] = Math.max(D[i - 1][j], D[i][j - 1]);
                    }
                }
            }
        }
        
        return D[lenA][lenB];    //D[lenA][lenB]代表最长公共子序列的长度
    }
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值