参考屈婉玲的youtube视频:https://www.youtube.com/watch?v=zkCHuCCoQEY
参考:《算法导论》P390页15.4 Longest common subsequence
问题描述:序列X={x1,x2,…,xn},Y={y1,y2,…,yn},当Z={z1,z2…,zn}是X的严格递增下标顺序(可以不连续)的子集,也是Y的严格递增下标顺序(可以不连续)的子集,则Z是X和Y的公共子序列。例如X={A,B,C,B,D,A,B},Y={B,D,C,A,B,A},{B,C,A}、{B,C,B,A}、{B,D,A,B}都是X和Y的公共子序列。其中最长的公共子序列叫做Longest common subsequence,即经典的LCS。
具体点:char[]xArray和char[] yArray是字符数组,长度分别为m、n,求他们的LCS
【分析】自顶向下分析,二维数组cTable[i][j]记录xArray[0~i],yArray[0~j]的最长公共子序列的长度,则cTable[m][n]记录xArray[0~m],yArray[0~n]的最长公共子序列的长度。
1) 如果xArray[m]=yArray[n],表明最后一个元素xArray[m]是LCS中的元素,xArray[0~m],yArray[0~n]的最长公共子序列=xArray[0~m-1],yArray[0~n-1]的最长公共子序列+1,即cTable[m][n]=cTable[m-1][n-1]。
2) 如果xArray[m]!=yArray[n],表明xArray[m]、yArray[n]都有可能是LCS中的元素,但不能同时是。如果xArray[m]可能是,则xArray[0~m],yArray[0~n]的最长公共子序列=xArray[0~m],yArray[0~n-1]的最长公共子序列;如果yArray[n]可能是,则xArray[0~m],yArray[0~n]的最长公共子序列=xArray[0~m-1],yArray[0~n]的最长公共子序列。即cTable[m][n]=max(cTable[m-1][n], cTable[m][n-1])。
状态递归方程为:
参考《算法导论》P394页伪代码,java实现如下:
*****************************************
控制台输出:
最大子数组为:BCBACBACB
长度为:9
--------------------------------------------------------------------------------------------------------
或者以下写法
class Untitled {
public static void main(String[] args) {
// TODO 自动生成的方法存根
String x="ABCBDABCBACABC";
String y="BDCABACABABCB";
int temp=getLCSLength(x, y);
System.out.println("\n长度为:"+temp);
}
public static int getLCSLength(String x,String y) {
char[] xArray=x.toCharArray();
char[] yArray=y.toCharArray();
int m=xArray.length;
int n=yArray.length;
int [][] bTable=new int[m][n];
/*cTable[i][j]记录xArray[0~i],yArray[0~j]的最长公共子序列的长度*/
int [][] cTable=new int[m+1][n+1];
for (int i = 0; i <=m; i++) {
for (int j = 0; j <=n; j++) {
if(i==0||j==0){
cTable[i][j]=0;
}else{
if (xArray[i-1]==yArray[j-1]) {
cTable[i][j]=cTable[i-1][j-1]+1;
bTable[i-1][j-1]=2;//相等标记为2
}else if (cTable[i-1][j]>=cTable[i][j-1]) {
cTable[i][j]=cTable[i-1][j];
bTable[i-1][j-1]=1;
}else {
cTable[i][j]=cTable[i][j-1];
bTable[i-1][j-1]=3;
}
}
}
}
System.out.print("最大子数组为:");
printLCS(xArray, bTable, m-1,n-1);
return cTable[m][n];
}
/*输出最佳路径即最长公共子序列*/
public static void printLCS (char[] xArray,int[][] bTable,int i,int j) {
if (i==-1||j==-1) {
return;
}
if (bTable[i][j]==2) {
printLCS(xArray, bTable, i-1, j-1);
System.out.print(xArray[i]);
}else if (bTable[i][j]==1) {
printLCS(xArray, bTable, i-1, j);
}else {
printLCS(xArray, bTable, i, j-1);
}
}
}
通过java在线运行环境http://run.jser.com/java.html
执行结果
最大子数组为:BCBACBACB
长度为:9
/**
* @param A, B: Two strings.
* @return: The length of longest common subsequence of A and B.
*/
public int longestCommonSubsequence(String A, String B) {
// write your code here
if (A == null || B == null) {
return 0;
}
int lenA = A.length();
int lenB = B.length();
int[][] D = new int[lenA + 1][lenB + 1];
for (int i = 0; i <= lenA; i++) {
for (int j = 0; j <= lenB; j++) {
if (i == 0 || j == 0) {
D[i][j] = 0;
} else {
if (A.charAt(i - 1) == B.charAt(j - 1)) {
D[i][j] = D[i - 1][j - 1] + 1;
} else {
D[i][j] = Math.max(D[i - 1][j], D[i][j - 1]);
}
}
}
}
return D[lenA][lenB]; //D[lenA][lenB]代表最长公共子序列的长度。
}
}