1、最长公共子序列(LCS)算法Java语言的简单实现:
String lcs(String x, String y) {//输入序列x,y;返回最长公共子序列
int[][] c = new int[x.length() + 1][y.length() + 1];//用来存放算法输出的结果值
char[] cx = x.toCharArray();
char[] cy = y.toCharArray();
StringBuilder lcs = new StringBuilder();
for (int i = 0; i <= cx.length; i++) {
c[i][0] = 0;
}
for (int i = 0; i <= cy.length; i++) {
c[0][i] = 0;
}
for (int i = 1; i <= x.length(); i++) {//算法实现
for (int j = 1; j <= y.length(); j++) {
if (cx[i-1] == cy[j-1]) {
c[i][j] = c[i-1][j-1] +1;
} else if (c[i-1][j] >= c[i][j-1]) {
c[i][j] = c[i-1][j];
} else {
c[i][j] = c[i][j-1];
}
}
}
Stack<Character> stack = new Stack<Character>();
int i = x.length() - 1;
int j = y.length() - 1;
while ((i >= 0) && (j >= 0)) {
if(cx[i] == cy[j]){//字符串从后开始遍历,如若相等,则存入栈中
stack.push(cx[i]);
i--;
j--;
} else {
if (c[i+1][j] >= c[i][j+1]) {//如果字符串的字符不同,则在数组中找相同的字符,注意:数组的行列要比字符串中字符的个数大1,因此i和j要各加1
j--;
} else {
i--;
}
}
}
while(!stack.isEmpty()){
lcs.append(stack.pop());
}
return lcs.toString();
}
时间复杂度为O(mn),空间复杂度为O(mn)。
算法思想(动态规划):
(1)空序列是任意两个序列的公共子序列;
(2)比较X[i],Y[j],若X[i]与Y[j]相同,则LCS(X(i), Y(j)) = LCS(X(i-1), Y(j-1)) + 1;
(3)比较X[i],Y[j],若X[i]与Y[j]不同,则LCS(X(i), Y(j)) = max(LCS(X(i), Y(j-1)),LCS(X(i-1), Y(j)))。
下面,就可以根据算法思想写出它的递归公式:
(1)c[i][j] = 0 (i=0, j=0)
(2)c[i][j] = c[i-1][j-1] + 1 (i>0, j>0, x[i] = y[j])
(3)c[i][j] = max(c[i][j-1] ,c[i-1][j]) (i>0, j>0, x[i] != y[j])
由此,可画出如下c[i][j]。
同样的,当要从c表内取出最长公共子序列时,可使用栈,从后向前遍历,根据递归公式,遍历出最长公共子序列,加入栈中,取出时正好是正向的最长公共子序列。
2、最长公共子串的Java实现
public static String lcsubstring(String x, String y) {
int n = 0;
int max = 0;
char[] a = x.toCharArray();
char[] b = y.toCharArray();
int[][] c = new int[a.length+1][b.length+1];
for (int i = 0; i < a.length; i++) {
c[i][0] = 0;
}
for (int j = 0; j < b.length; j++) {
c[0][j] = 0;
}
for (int i = 0; i < a.length; i++) {
for (int j = 0; j < b.length; j++) {
if (a[i] == b[j]) {
c[i+1][j+1] = c[i][j] +1;
if (max < c[i+1][j+1]) {
max = c[i+1][j+1];
n = i+1;
}
} else {
c[i+1][j+1] = 0;
}
}
}
return x.substring(n-max, n);
}
时间复杂度为O(mn),空间复杂度为O(mn)。前提是使用动态规划(DP),如果想要暴力计算,会更消耗时间。它的动态规划要比最长公共子序列简单,理解了最长公共子序列,最长公共子串就很好理解了。
公式:
(1)c[i][j] = 0 (i>=0, j>=0, x[i+1] != y[j+1])
(2)c[i][j] = c[i-1][j-1] + 1 (i>0, j>0, x[i] = y[j])
最后,要取出最长公共子串,可以找一个值max标记当前最长的子串长度,一个值标记最长的子串结尾的位置。由这两个值就可以取出最长公共子串了。