最长公共子序列 LCS

最长公共子序列,英文缩写为LCS(Longest Common Subsequence)。其定义是,一个数列 S ,如果分别是两个或多个已知数列的子序列,且是所有符合此条件序列中最长的,则 S 称为已知序列的最长公共子序列。
动态规划的一个计算最长公共子序列的方法如下:
X=<X1,X2,……,Xm> ,Y=<Y1,Y2,……,Yn> and Z=<Z1,Z2,……,Zk>
二维数组 Zi,j表示 X 的 i 位和 Y 的 j 位之前的最长公共子序列的长度,则有:

*If  Xi=Yj,then Zk=Xm=Yn and Zk-1 is an LCS of Xm-1 and Yn-1;

*If  Xm!=Yn,then Zk!=Xm implies that Z is an LCS of Xm-1 and Y;

*If  Xm!=Yn,then Zk!=Yn implies that Z is an LCS of X and Yn-1;

 

定义C[i,j]为序列Xi和Yj的LCS的长度,可以有以下结论:        

*若i=0或j=0,C[i,j]=0;

*若i,j>0 且Xi=Yj,C[i,j]=C[i-1,j-1]+1;

*若i,j>0且Xi!=Yj,C[i,j]=max{C[i,j-1],C[i-1,j]}

 

通过上述结论可知,当Xi=Yj时,我们的目标是找出Xi-1和Yj-1的LCS,当Xi!=Yj时,是找出Xi和Yj-1的LCS或是Xi-1和Yj的LCS,依据该数组回溯,便可找出最长公共子序列。该算法的空间、时间复杂度均为O(nm),经过优化后,空间复杂度可为O(n),时间复杂度为O(nlogn)

 

 

Java 实现:

 

package com.scgm.java.algorithms;

 

 public class Test {

        String str1 = "abcdefghijklmnopqjgdksgdsafjdlksajgldsnagjklvjcxioubdsa";

        String str2 = "abcdefgqdsagdjsklajgkaiowuioagdsaklgnewa";

        

        //表C用于存放字符比较后的数;表b用于存放存放路径

        int[][] c = new int[str1.length()+1][str2.length()+1];

        String[][] b = new String[str1.length()+1][str2.length()+1];

        

        //计算最长公共序列的长度以及记录路径

        void LCS(String s1,String s2){

               for(int i=0;i<s1.length();i++){

                      c[i][0] = 0;

               }

               

               for(int j=0;j<s2.length();j++){

                      c[0][j] = 0;

               }

               

               for(int i=0;i<s1.length();i++){

                      for(int j=0;j<s2.length();j++){

                             //字符相等,表中数据加1,用“↖”表示从对角线返回

                             if(s1.charAt(i)==s2.charAt(j)){

                                    c[i+1][j+1] = c[i][j]+1;

                                    b[i+1][j+1] = "↖";

                             }

                   //当前两个字符不相等,表中数据继承上方的数据,用“↑”表示向上返回

                             else if(c[i][j+1]>=c[i+1][j]){

                                    c[i+1][j+1] = c[i][j+1];

                                    b[i+1][j+1] = "↑";

                             }

                             //表中数据继承左方的数据,用“←”表示向左返回

                             else{

                                    c[i+1][j+1] = c[i+1][j];

                                    b[i+1][j+1] = "←";

                             }

                      }

               }

        }//end LCS

        

        //输出最长公共序列

        void print_LCS(String[][] b,String s1,int str1_length,int str2_length){

               //其中一个字符串为空,返回

               if(str1_length==-1||str2_length==-1){

                      return;

               }

               //对角线返回

               if(b[str1_length+1][str2_length+1]=="↖"){

                      print_LCS(b,s1,str1_length-1,str2_length-1);

                      System.out.print(s1.charAt(str1_length));

               }

               //向上返回

               else if(b[str1_length+1][str2_length+1]=="↑"){

                            print_LCS(b,s1,str1_length-1,str2_length);

               }

               //向左返回

               else

                      print_LCS(b,s1,str1_length,str2_length-1);

        }//end print_LCS


        public static voidmain(String[] args){

               Test t = new Test();

               t.LCS(t.str1, t.str2);

               t.print_LCS(t.b, t.str1, t.str1.length()-1, t.str2.length()-1);

        }

}

 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值