输入:X=ABCHFGASJKXBD Y=DBDHASXB
输出:LCS=BHASXB
【分析】
最长公共子序列LCS问题可用动态规划算法有效解决。而首先我们需要证明的是,使用动态规划算法的两个基本要素,即为什么最长公共子序列LCS问题可以用动态规划算法来解决。
最长公共子序列问题具有最优子结构性质:
设序列X={x1,x2,x2,……,xm}和Y={y1,y2,y3,……,yn},而Z={z1,z2,z3,……,zk}是X和Y的最长公共子序列。则:
若xm = yn,则zk = xm = yn,Zk-1为Xm-1 和Yn-1的最长公共子序列。
(证:采用反证法,若zk != xm,那么有{z1,z2,z3,……,zk,xm}是为X和Y长度为k+1的最长公共子序列,这与Z是X和Y长度为k的最长公共子序列矛盾。
若Zk-1不是Xm-1和Yn-1的最长公共子序列,则Xm-1和Yn-1具有长度大于k-1的最长公共子序列,那么加上xm后,X和Y的最长公共子序列的长度应该大于k,这与Z是X和Y长度为k的最长公共子序列矛盾。)
若xm != yn 且 zk != xm,则Z是Xm-1和Y的最长公共子序列。
(证:采用反证法,若Z不是Xm-1和Y的最长公共子序列,则Xm-1和Y具有长度大于k的最长公共子序列,那么X和Y的最长公共子序列的长度必定也大于k。这与X和Y具有长度为k的最长公共子序列Z矛盾。)
若xm != yn 且 zk != yn,则Z是X和Yn-1的最长公共子序列。
(证:采用反证法,证法同上述类似)
最长公共子序列问题具有子问题重叠性质。
在寻找X和Y的最长公共子序列时,我们要进行分类讨论:
当xm = yn时,我们要找出Xm-1和Yn-1的最长公共子序列,在其尾部加上xm就为X和Y的最长公共子序列;
当xm != yn时,我们要找出 Xm-1和Y的最长公共子序列和X和Yn-1的最长公共子序列,再比较两者哪个大,即为X和Y的最长公共子序列。
我们可以从中看出,当我们要查找Xm-1和Y的最长公共子序列时,同样也要按照上面所说的步骤进行查找,即有可能要继续查找Xm-2和Y、Xm-1和Yn-1的最长公共子序列;当我们要X和Yn-1的最长公共子序列时,同样也要按照上面所说的步骤进行查找,即有可能要继续查找Xm-1和Yn-1、X和Yn-2的最长公共子序列。在这两个子问题都包含了一个公共子问题,即计算Xm-1和Yn-1的最长公共子序列。
建立LCS问题的递推关系,用c[i][j]表示Xi和Yj的最长公共子序列的长度。其中Xi={x1,x2,……,xi},Yj={y1,y2,……,yj}。
c[i][j] = 0, i = j = 0;
c[i][j] = c[i - 1][j - 1] + 1, xi = yj;
c[i][j] = max{c[i - 1][j], c[i][j - 1]}, xi != yj;
根据递推关系,我们可以写出程序。
【程序】用java语言编写程序,代码如下:
import java.io.BufferedInputStream;
import java.util.Scanner;
public class LCS {
public static void main(String[] args) {
Scanner input = new Scanner(new BufferedInputStream(System.in));
while(input.hasNext()) {
int m = input.nextInt();
String sx = input.next();
char[] x = new char[m + 1];
for(int i = 1; i < m + 1; i++)
x[i] = sx.charAt(i - 1);
int n = input.nextInt();
String sy = input.next();
char[] y = new char[n + 1];
for(int i = 1; i < n + 1; i++)
y[i] = sy.charAt(i - 1);
/*int[][] c = new int[m + 1][n + 1];
for(int i = 0; i < m + 1; i++)
for(int j = 0; j < n + 1; j++)
c[i][j] = -1;
int len = LCS(m, n, x, y, c);
System.out.println();
System.out.println(len);*/
int[][] c = new int[m + 1][n + 1];
int[][] b = new int[m + 1][n + 1];
LCSlength(m, n, x, y, c, b);
printLCS(m, n, b, x);
System.out.println();
System.out.println(c[m][n]);
}
}
/*public static int LCS(int m, int n, char[] x, char[] y, int[][] c) {
if(m == 0 || n == 0) {
c[m][n] = 0;
return 0;
}
//if(c[m][n] != -1)
//return c[m][n];
if(x[m] == y[n]) {
c[m][n] = LCS(m - 1, n - 1, x, y, c) + 1;
System.out.print(x[m]);
System.out.println(m + " " + n);
}
else {
int lcs1 = LCS(m, n - 1, x, y, c);
int lcs2 = LCS(m - 1, n, x, y, c);
if(lcs1 > lcs2)
c[m][n] = lcs1;
else
c[m][n] = lcs2;
}
return c[m][n];
}*/
public static void LCSlength(int m, int n, char[] x, char[] y, int[][] c, int[][] b) {
int i, j;
for(i = 1; i <= m; i++)
c[i][0] = 0;
for(j = 1; j <= n; j++)
c[0][j] = 0;
for(i = 1; i <= m; i++)
for(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;
}
}
}
}
public static void printLCS(int i, int j, int[][] b, char[] x) {
if(i == 0 || j == 0)
return;
if(b[i][j] == 1) {
printLCS(i - 1, j - 1, b, x);
System.out.print(x[i]);
}
else if(b[i][j] == 2)
printLCS(i - 1, j, b, x);
else
printLCS(i, j - 1, b, x);
}
}