动态规划合集:
1.矩阵链乘法
2.投资组合问题
3.完全背包问题
4.01背包问题
5.最长公共子序列
例题5——最长公共子序列
描述
给定序列
求X和Y的最长公共子序列。
实例:
X:A B C B D A B
Y:B D C A B A
最长公共子序列为:BCBA,长度为4
问题分析
以做一般性说明,其中Z表示XY的最长公共子序列,一定有下述条件:
①若,且
是
的LCS。
②若,
是
与
的LCS。
③若是
与
的LCS。
令表示
的LCS的长度,那么递推表达式可以写成:
标记函数为:

算法实现
public class LongestCommonSubsequence {
public static int [][]mem;
public static int [][]s;
public static int [] result; // 记录子串下标
public static int LCS(char []X,char []Y,int n,int m){
for (int i = 0; i <= n; i++) {
mem[i][0] = 0;
s[i][0] = 0;
}
for (int i = 0; i <= m; i++) {
mem[0][i] = 0;
s[0][i] = 0;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m ; j++) {
if (X[i-1] == Y[j-1]){
mem[i][j] = mem[i-1][j-1] + 1;
s[i][j] = 1;
}
else {
mem[i][j] = Math.max(mem[i][j-1],mem[i-1][j]);
if (mem[i][j] == mem[i-1][j]){
s[i][j] = 2;
}
else s[i][j] = 3;
}
}
}
return mem[n][m];
}
// 追踪解
public static void trace_solution(int n,int m){
int i = n;
int j = m;
int p = 0;
while (true){
if (i== 0 || j == 0) break;
if (s[i][j] == 1 ){
result[p] = i;
p++;
i--;j--;
}
else if (s[i][j] == 2){
i--;
}
else { //s[i][j] == 3
j--;
}
}
}
public static void print(int [][]array,int n,int m){
for (int i = 0; i < n + 1; i++) {
for (int j = 0; j < m + 1; j++) {
System.out.printf("%d ",array[i][j]);
}
System.out.println();
}
}
public static void main(String[] args) {
char []X = {'A','B','C','B','D','A','B'};
char []Y = {'B','D','C','A','B','A'};
int n = X.length;
int m = Y.length;
// 这里重点理解,相当于多加了第一行 第一列。
mem = new int[n+1][m+1];
// 1 表示 左上箭头 2 表示 上 3 表示 左
s = new int[n+1][m+1];
result = new int[Math.min(n,m)];
int longest = LCS(X,Y,n,m);
System.out.println("备忘录表为:");
print(mem,n,m);
System.out.println("标记函数表为:");
print(s,n,m);
System.out.printf("longest : %d \n",longest);
trace_solution(n,m);
// 输出注意 result 记录的是字符在序列中的下标
for (int k = longest-1; k >=0 ; k--) {
// 还需要再减一 才能跟 X Y序列对齐。
int index = result[k]-1;
System.out.printf("%c ",X[index]);
}
}
}
网友评论