最长公共子序列,英文缩写为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);
}
}