(一)LCS,Longest Common Subsequence,即最长公共子序列
问题描述:一个序列 S ,如果分别是两个或多个已知序列的子序列,且是所有符合此条件序列中最长的,则 S 称为已知序列的最长公共子序列。而最长公共子串(要求连续)和最长公共子序列是不同的。
算法思路:动态规划问题,最重要的是递归的思想,动态规划就死递归地解决公共子问题。
递归公式
LCS(X[1..n],y[1..m]) =
(1) LCS(X[1..n-1],y[1..m-1]) 当x[n]=y[n];
(2) max(LCS(X[1..n],y[1..m-1]),LCS(X[1..n-1],y[1..m])) 当x[n]≠y[n];
代码:
public class LCSProblem {
public static final int UP = 1;
public static final int LEFT = 2;
public static final int LEFT_UP = 3;
public static int LCS(char[] a, char[] b) {
int la = a.length;
int lb = b.length;
int[][] lcsMatrix = new int[la][lb];
int[][] pathMatrix = new int[la][lb];
for (int i = 0; i < la; i++) {
for (int j = 0; j < lb; j++) {
if (i == 0 || j == 0) {
lcsMatrix[i][j] = a[i] == b[j] ? 1 : 0;
if (a[i] == b[j])
pathMatrix[i][j] = LEFT_UP;
continue;
}
if (a[i] == b[j]) {
lcsMatrix[i][j] = lcsMatrix[i - 1][j - 1] + 1;
pathMatrix[i][j] = LEFT_UP;
} else {
if (lcsMatrix[i][j - 1] > lcsMatrix[i - 1][j]) {
lcsMatrix[i][j] = lcsMatrix[i][j - 1];
pathMatrix[i][j] = LEFT;
} else {
lcsMatrix[i][j] = lcsMatrix[i - 1][j];
pathMatrix[i][j] = UP;
}
}
}
}
for (int i = 0; i < pathMatrix.length; i++) {
for (int j = 0; j < pathMatrix[i].length; j++) {
System.out.print(pathMatrix[i][j] + " ");
}
System.out.println();
}
printLCS(a, la - 1, lb - 1, pathMatrix);
return lcsMatrix[la - 1][lb - 1];
}
public static void printLCS(char a[], int i, int j, int[][] pathMatrix) {
if (i < 0 || j < 0) {
return;
}
if (pathMatrix[i][j] == LEFT_UP) {
printLCS(a, i - 1, j - 1, pathMatrix);
System.out.print(a[i]);
}
if (pathMatrix[i][j] == UP) {
printLCS(a, i - 1, j, pathMatrix);
}
if (pathMatrix[i][j] == LEFT) {
printLCS(a, i, j - 1, pathMatrix);
}
}
public static void main(String[] args) {
System.out.println(LCS("ABCBDAB".toCharArray(), "BDCABA".toCharArray()));
}
}
算法思路:依然是动态规划。这个问题和上一篇文章所讨论的“最大子串和”有类似之处,所不同的是最大自串和所有的操作都可以在一维实现,所以算法复杂度可以降到O(n),而LCS+的操作是两个数组的比较,所以复杂度为O(n2)。
递归公式:
定义矩阵m,m[i][j]记录包含a[i]和b[j]的当前最长子串长度。
m[i][j]=a[i]==b[j]?m[i][j]+1:0;
LCS+(i,j)=max(
LCS+(i-1,j-1),m[i][j])
代码:
public class LCSProblemPlus {
public static void main(String[] args) {
String a = "124aabcasd";
String b = "324aabc";
System.out.println(LCSPlus(a, b));
}
public static int LCSPlus(String a, String b) {
int[][] m = new int[a.length()][b.length()];
int maxLen = 0;
int imark = 0;
int jmark = 0;
// 如何记录maxLenEndingHere
for (int i = 0; i < a.length(); i++) {
for (int j = 0; j < b.length(); j++) {
if (i == 0 || j == 0) {
if (a.charAt(i) == b.charAt(j)) {
m[i][j] = 1;
}
} else {
if (a.charAt(i) == b.charAt(j)) {
m[i][j] = m[i - 1][j - 1] + 1;
} else {
m[i][j] = 0;
}
}
if (maxLen < m[i][j]) {
maxLen = m[i][j];
imark = i;
jmark = j;
}
}
}
printLCSPlus(a, imark, jmark, m);
return maxLen;
}
public static void printLCSPlus(String a, int imark, int jmark, int[][] m) {
if (m[imark][jmark] != 0 && imark >= 0 && jmark >= 0) {
printLCSPlus(a, imark-1, jmark-1, m);
System.out.print(a.charAt(imark));
}
}
}