最长公共子序列问题
问题介绍
给定一个序列X=< x1,x2…xm>.另一个序列Z=< z1,z2…zk >满足如下条件时称为,x的子序列:存在一个严格递增的X的 下标序列i1, i2,…ik对所有的j= 1,2… k满足x[i,j]= z[j].给定两个列X和Y ,如果同时是X和Y的子列,则称是X和Y的公共子序列。最长公共子序列(LCS)问题指的是:求解两个列X和Y的长度最长的公共子序列。
例如,序列X= {A,B,C, B, D,A, B}和Y = {B, D, C, A, B, A}的最长公共子序列为{B,C, B,A},长度为4。
本文将具体阐释如何用动态规划法(Dynamic Programming)来求解最长公共子列(LCS) 问题。
问题分析
假设两字符串str1和str2的长度分别为n和m。对于这类问题,我们一般可以构建一个大小n*m的矩阵dp,其中dp[i][j]代表的是str1中前i个字符串与str2中前j个字符串的最长公共子序列的长度。
代码实现:
import java.util.Arrays;
import java.util.List;
public class LCS {
// 主函数
public static void main(String[] args) {
// 两个序列X和Y
List<String> X = Arrays.asList("A","B","C","B","D","A","B");
List<String> Y = Arrays.asList("B","D","C","A","B","A");
int m = X.size(); //X的长度
int n = Y.size(); // Y的长度
String[][] b = LCS_length(X, Y); //获取维护表b的值
print_LCS(b, X, m, n); // 输出LCS
}
/*
函数LCS_length:获取维护表b的值
传入参数: 两个序列X和Y
返回值: 维护表b
*/
public static String[][] LCS_length(List X, List Y){
int m = X.size(); //X的长度
int n = Y.size(); // Y的长度
int[][] c = new int[m+1][n+1];
String[][] b = new String[m+1][n+1];
// 对表b和表c进行初始化
for(int i=1; i<m+1; i++){
for(int j=1; j<n+1; j++){
c[i][j] = 0;
b[i][j] = "";
}
}
// 利用自底向上的动态规划法获取b和c的值
for(int i=1; i<m+1; i++){
for(int j=1; j<n+1; j++){
if(X.get(i-1) == Y.get(j-1)){
c[i][j] = c[i-1][j-1]+1;
b[i][j] = "diag";
}
else if(c[i-1][j] >= c[i][j-1]){
c[i][j] = c[i-1][j];
b[i][j] = "up";
}
else{
c[i][j] = c[i][j-1];
b[i][j] = "left";
}
}
}
return b;
}
// 输出最长公共子序列
public static int print_LCS(String[][] b, List X, int i, int j){
if(i == 0 || j == 0)
return 0;
if(b[i][j].equals("diag")){
print_LCS(b, X, i-1, j-1);
System.out.print(X.get(i-1)+" ");
}
else if(b[i][j].equals("up"))
print_LCS(b, X, i-1, j);
else
print_LCS(b, X, i, j-1);
return 1;
}
}
`