给定俩个序列X,Y,求出俩序列中最长公共子序列,子序列可以是不连续的。拿到这样的问题,如果使用暴力法,是枚举出X所有的子序列,判断子序列是否为Y的子序列,然后再从所有的子序列中找出长度最长的。这样做的话,时间复杂度为O(2^N),时间复杂度太高。在这种通过暴力法解决时,时间复杂度过高的情况下,我们可以考虑动态规划法。
序列:
X={x1,x2,,,xn}
Y={y1,y2,,,ym}
1.确定子问题
采用动态规划第一步确定子问题,求Xn和Ym的最长公共子序列
如果xn = ym,Xn与Ym的最长公共子序列问题转换为求Xn-1与Ym-1的最长公共子序列
如果xn != ym,Xn与Ym的公共子序列问题转换为求Xn与Ym-1,Xn-1与Ym中的最长公共子序列问题
2.状态转移方程
dp[i][j] = dp[i-1][j-1]+1 如果x[i] = y[j]
dp[i][j] = max{dp[i][j-1],dp[i-1][j]} 如果x[i] != y[j]
其中dp数组中存放的是长度
上述dp数组记录了最长公共子序列长度的演变过程,最长公共子序列长度为DP[lengthX][lengthY],如何获取最长公共子序列,从DP[lengthX][lengthY]出发,还原其生成过程,当然这里存在多种生成方式,我们取其中一种。
4.代码
这里采用了一个辅助数组temp[][]用于构造LCS。
/***
* 最长公共子序列
*/
public class LongestCommanSeq {
public static void main(String[] args){
char[] a = {'A','B','C','B','D','A','B'};
char[] b = {'B','D','C','A','B','A'};
getLCS(a,b);
}
/**
* 最长公共子序列
* @param a
* @param b
*/
private static void getLCS(char[] a, char[] b) {
if (a == null || b == null || a.length == 0 || b.length == 0){
System.out.println("no LCS");
return;
}
int lengthA = a.length;
int lengthB = b.length;
int[][] dp = new int[lengthA+1][lengthB+1];
char[][] temp = new char[lengthA+1][lengthB+1];
for (int i = 1; i < lengthA+1; i++){
for (int j = 1; j < lengthB+1; j++){
if (a[i-1] == b[j-1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
temp[i][j] = 'x';
}
else {
if (dp[i][j-1] >= dp[i-1][j]){
dp[i][j] = dp[i][j-1];
temp[i][j] = 'z';
}else {
dp[i][j] = dp[i-1][j];
temp[i][j] = 's';
}
}
}
}
System.out.println(dp[lengthA][lengthB]);
printLCS(temp,a,lengthA,lengthB);
printLCSS(dp,a,lengthA,lengthB);
}
private static void printLCS(char[][] temp,char[] a,int i,int j) {
if (i == 0 || j == 0)
return;
if (temp[i][j] == 'x') {
printLCS(temp,a, i - 1,j - 1);
System.out.print(a[i-1]+" ");
}else if(temp[i][j] == 's')
printLCS(temp,a,i-1,j);
else
printLCS(temp,a,i,j-1);
}
}
优化:由于LCS构造只与dp[i-1][j-1],dp[i-1][j],dp[i][j-1]有关,所以可以优化空间,改造打印LCS方法
private static void printLCSS(int[][] dp, char[] a, int i, int j) {
if (i == 0 || j == 0)
return;
if (dp[i][j] == dp[i-1][j-1]+1){
printLCSS(dp,a,i-1,j-1);
System.out.print(a[i-1]+" ");
}else if (dp[i][j] == dp[i-1][j]){
printLCSS(dp,a,i-1,j);
}else {
printLCSS(dp,a,i,j-1);
}
}