1.最长公共子序列结构
设序列X = { x1,x2,x3,……,xm}和序列Y = {y1,y2,y3,…….,yn}的最长公共子序列为序列Z = {z1,z2,…….,zk},则:
(1)若xm = yn,则zk = xm = yn,且zk-1是xm-1和yn-1的最长公共子序列;
(2)若xm != yn,且zk != xm,则Z是xm-1和yn的最长公共子序列;
(3)若xm != yn,且zk != yn,则Z是xm和yn-1的最长公共子序列;
可知,两个序列的最长公共子序列包含了其前缀的最长公共子序列,故最长公共子序列问题具有最优子结构性质。
2.子问题的递归结构
由最长公共子序列的最优子结构性质构建子问题最优值的递归关系:
用C[i][j]记录序列X[i]和序列Y[j]的最长公共子序列的长度:
X[i]={x1,x2,…..,xi}; Y[j]={y1,y2,……yj}
当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-1][j], C[i][j-1]};
3.计算最优值
在所考虑的子问题空间中,一共有m*n个不同的子问题,用动态规划法自底向上地计算最优值能提高算法效率。
计算最长公共子序列长度的动态规划算法lcsLength:
输入序列: X={x1,x2,…,xm}和序列Y ={y1,y2,….yn}
输出两个数组C[i][j]和b[i][j],C [i][j]中记录最长公共子序列的长度,b[i][j]记录C[i][j]的解是由哪一个子问题的解得到的,在构造最长公共子序列时用到。
package lcsLength;
import java.util.Scanner;
public class LcsLength {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner scanner = new Scanner(System.in);
System.out.print("请输入字符串1:");
String s1 = scanner.next();
System.out.print("请输入字符串2:");
String s2 = scanner.next();
char []x = s1.toCharArray();
char []y = s2.toCharArray();
int [][]c = new int[x.length+1][y.length+1];
int [][]b = new int[x.length+1][y.length+1];
LcsLength(x,y,c,b);
}
/*
* 统计最长公共子序列长度
*/
public static void LcsLength(char []x,char []y,int [][]c,int [][]b){
int m = x.length-1;
int n = y.length-1;
for(int i = 1;i <= m;i++) c[i][0] = 0;
for(int j = 1;j <= n;j++) c[0][j] = 0;
for(int i = 1;i <= m;i++){
for(int j = 1;j <= n;j++){
if(x[i] == y[j]) {c[i][j] = c[i-1][j-1] + 1;b[i][j] = 1;}
else if(c[i-1][j] >= c[i][j-1]) {c[i][j] = c[i-1][j];b[i][j] = 2;}
else {c[i][j] = c[i][j-1];b[i][j] = 3;}
}
}
System.out.print("最长子序列为:");
Lcs(m,n,x,b);
}
/*
* 构建最长公共子序列
*/
public static void Lcs(int i,int j,char []x,int [][]b){
if (i==0 || j==0) return;
if (b[i][j] ==1) {
Lcs(i-1,j-1,x,b);
System.out.print(x[i]);
}
else if (b[i][j] == 2) {
Lcs(i-1,j,x,b);
}
else Lcs(i,j-1,x,b);
}
}
运行结果: