一、LCS算法代码
/**
* 通过动态规划的思想求出字符串 str1 和 str2 的最长公共子序列
* 空间复杂度 O(nm)
* 时间复杂度 O(nm)
*/
public static String getLCS(String str1, String str2) {
int len1 = str1.length();
int len2 = str2.length();
int[][] dp = new int[len1][len2];
// 初始化第一行
for (int i = 0; i < len1; i++) {
if (str1.charAt(i) == str2.charAt(0)) {
dp[i][0] = 1;
} else if (i != 0) {
dp[i][0] = dp[i - 1][0];
} else {
dp[i][0] = 0;
}
}
// 初始化第一列
for (int j = 0; j < len2; j++) {
if (str1.charAt(0) == str2.charAt(j)) {
dp[0][j] = 1;
} else if (j != 0) {
dp[0][j] = dp[0][j - 1];
} else {
dp[0][j] = 0;
}
}
// 通过动态规划的思想,求出 dp 数组
for (int i = 1; i < len1; i++) {
for (int j = 1; j < len2; j++) {
if (str1.charAt(i) == str2.charAt(j)) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
// 反向遍历 dp 数组,构造出最长公共子序列
StringBuilder sb = new StringBuilder();
int i = len1 - 1;
int j = len2 - 1;
while (i >= 0 && j >= 0) {
if (str1.charAt(i) == str2.charAt(j)) {
sb.append(str1.charAt(i));
i--;
j--;
} else if (i > 0 && dp[i][j] == dp[i - 1][j]) {
i--;
} else if (j > 0 && dp[i][j] == dp[i][j - 1]) {
j--;
}
}
return sb.reverse().toString();
}
二、LCS算法C语言
/*
* 通过动态规划的思想求出字符串 s1 和 s2 的最长公共子序列
* 空间复杂度 O(nm)
* 时间复杂度 O(nm)
*/
char* lcs(char *s1, char *s2) {
int len1 = strlen(s1);
int len2 = strlen(s2);
int dp[len1][len2];
// 初始化第一行
for (int i = 0; i < len1; i++) {
if (s1[i] == s2[0]) {
dp[i][0] = 1;
} else if (i != 0) {
dp[i][0] = dp[i - 1][0];
} else {
dp[i][0] = 0;
}
}
// 初始化第一列
for (int j = 0; j < len2; j++) {
if (s1[0] == s2[j]) {
dp[0][j] = 1;
} else if (j != 0) {
dp[0][j] = dp[0][j - 1];
} else {
dp[0][j] = 0;
}
}
// 通过动态规划的思想,求出 dp 数组
for (int i = 1; i < len1; i++) {
for (int j = 1; j < len2; j++) {
if (s1[i] == s2[j]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
// 反向遍历 dp 数组,构造出最长公共子序列
char* res = (char*) malloc(sizeof(char) * (dp[len1 - 1][len2 - 1] + 1));
res[dp[len1 - 1][len2 - 1]] = '\0';
int i = len1 - 1, j = len2 - 1;
while (i >= 0 && j >= 0) {
if (s1[i] == s2[j]) {
res[dp[i][j] - 1] = s1[i];
i--;
j--;
} else if (i > 0 && dp[i][j] == dp[i - 1][j]) {
i--;
} else if (j > 0 && dp[i][j] == dp[i][j - 1]) {
j--;
}
}
return res;
}
三、LCS算法公式
LCS(str1, str2) = LCS(str1-1, str2-1) + 1, if str1[m] = str2[n]
LCS(str1, str2) = max(LCS(str1-1, str2), LCS(str1, str2-1)), if str1[m] ≠ str2[n]
四、LCS算法伪代码
算法输入:两个字符串 S 和 T
算法输出:S 和 T 的最长公共子序列
dp[i][j] 表示 S[1:i] 和 T[1:j] 的最长公共子序列的长度
for i in 1 to |S|
for j in 1 to |T|
if S[i] == T[j]
dp[i][j] = dp[i-1][j-1] + 1
else
dp[i][j] = max(dp[i-1][j], dp[i][j-1])
end for
五、LCS算法复杂度
根据伪代码可以得出,LCS算法的时间复杂度是 O(nm),空间复杂度也是 O(nm)。
六、LCS算法C表和B表
在 LCA 算法中,B 表就是一个记录点和深度信息的数组。在 LCS 算法中,B 表是一个记录选择路径信息的数组,用于还原 LCS。C 表是记录动态规划状态的数组,用于计算 LCS 长度。
/* 简化版 B表 */
for (int i = 1; i <= len1; i++) {
for (int j = 1; j <= len2; j++) {
if (str1.charAt(i - 1) == str2.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1] + 1;
b[i][j] = '↖';
} else if (dp[i - 1][j] >= dp[i][j - 1]) {
dp[i][j] = dp[i - 1][j];
b[i][j] = '↑';
} else {
dp[i][j] = dp[i][j - 1];
b[i][j] = '←';
}
}
}
七、LCS算法怎么输出
通过反向遍历 B 表,就能找到 LCS 了。
StringBuilder sb = new StringBuilder();
int i = len1;
int j = len2;
while (i > 0 && j > 0) {
switch (b[i][j]) {
case '↖':
sb.append(str1.charAt(i - 1));
i--;
j--;
break;
case '↑':
i--;
break;
case '←':
j--;
break;
default:
break;
}
}
return sb.reverse().toString();
八、LCS算法是什么意思
LCS 的全称是 Longest Common Subsequence,即最长公共子序列,是一种计算两个序列之间的最长公共子序列的算法。
九、LCS排名
在字符串匹配领域,LCS 算法是一种经典的方法,被广泛应用于文本编辑器、代码比较工具、版本控制工具等领域。同时,LCS 算法也是一个经典的动态规划问题,被用作编程面试及其他计算机科学领域的课程提及。