最长公共子序列算法应用非常广泛:比如染色体中DNA片段的的研究,基于最长公共子序列的微博谣言溯源研究等等。所以掌握与应用该基础算法是十分必要的。下面就简要阐述我对它的见解。
最长公共子序列的定义:
给定两个字符串:如
1.A B D C A C B
2.B A D B C
找到字符串1和字符串2的最长公共子序列
必要条件(1):子序列必须是有序的;
(2) : 子序列的长度最大;
注意 : 最长公共子序列不唯一
字符串1和字符串2的最长公共子序列: (1)B A C ,(2) A D B (3)…等等
应用穷举法可以得到我们想要的答案。但是时间复杂度为O(2^m*n)。是指数级别的复杂度,对于长序列是不适用的。因此我们使用动态规划法来求解。
刻画最长公共子序列问题的最优子结构
设X=x1x2…xm和Y=y1y2…yn是两个序列,Z=z1z2…zk是这两个序列的一个最长公共子序列。
1. 如果xm=yn,那么zk=xm=yn,且Zk-1是Xm-1,Yn-1的一个最长公共子序列;
2. 如果xm≠yn,那么zk≠xm,意味着Z是Xm-1,Y的一个最长公共子序列;
3. 如果xm≠yn,那么zk≠yn,意味着Z是X,Yn-1的一个最长公共子序列。
递归定义最优解
自底向上求最长公共子串长度
package 动态规划;
public class 自底向上求最长公共子串长度 {
public static int lcs(char[] x,char[] y,int i,int j,int[][] bak)
{
for(int m = 0; m <= i; m++)
{
for(int n = 0 ; n <= j; n++)
{
if(m == 0 || n ==0) bak[m][n] = 0;
else if(x[m] == y[n]) bak[m][n] = bak[m-1][n-1]+1;
else bak[m][n] = max (bak[m-1][n],bak[m][n-1]);
}
}
return bak[i][j];
}
private static int max(int a, int b) {
if(a > b) return a;
return b;
}
public static void main(String[] args) {
String s1 = "ABCBDAB";
char[] c1 = new char[s1.length() + 1];//带0号字符的字符数组
char[] t1 = s1.toCharArray(); //toCharArray() 方法将字符串转换为字符数组
c1[0] = (char)0;
for(int i = 1; i < t1.length; i++)
{
c1[i + 1] = t1[i];
}
String s2 = "BDCABA";
char[] c2 = new char[s2.length() + 1];//带0号字符的字符数组
char[] t2 = s2.toCharArray();
c2[0] = (char)0;
for(int i = 1; i < t2.length; i++)
{
c2[i + 1] = t2[i];
}
int[][] bak = new int[c1.length][c2.length];
System.out.println(lcs(c1,c2,c1.length-1 ,c2.length-1 ,bak));
}
}
备忘录法求最长公共子串长度
/***********************************************/
package 动态规划;
public class 备忘录法求最长公共子串长度 {
//缺点是复杂度与自底向上的算法一样,但实际上更占资源,时间上慢
public static int lcs(char[] x,char[] y,int i,int j,int[][] bak)
{
if(bak[i][j] != -1) return bak[i][j];
if(i == 0 || j == 0) return bak[i][j] = 0;
else if(x[i] == y[j]) return bak[i][j] = lcs(x,y,i-1,j-1,bak)+1;
else bak[i][j] = max(lcs(x,y,i-1,j,bak),lcs(x,y,i,j-1,bak));
return bak[i][j];
}
private static int max(int a, int b) {
if(a > b) return a;
return b;
}
public static void main(String[] args) {
String s1 = "ABCBDAB";
char[] c1 = new char[s1.length() + 1];//带0号字符的字符数组
char[] t1 = s1.toCharArray();
c1[0] = (char)0;
for(int i = 1; i < t1.length; i++)
{
c1[i + 1] = t1[i];
}
String s2 = "BDCAABA";
char[] c2 = new char[s2.length() + 1];//带0号字符的字符数组
char[] t2 = s2.toCharArray();
c2[0] = (char)0;
for(int i = 1; i < t2.length; i++)
{
c2[i + 1] = t2[i];
}
int[][] bak = new int[c1.length][c2.length];
for(int i = 0; i < c1.length; i++)
{
for(int j = 0 ; j < c2.length; j++)
{
bak[i][j] = -1;
}
}
System.out.println(lcs(c1,c2,c1.length -1 ,c2.length - 1,bak));
}
}