问题:
描述的是在两个序列中找到一个最长的子序列,使得该子序列在两个原序列中都出现,但不一定是连续的。
示例:
对于字符串 "ABCDGH" 和 "AEDFHR",它们的最长公共子序列是 "ADH"。在这个例子中,两个字符串中的字符 "A"、"D" 和 "H" 出现在了最长公共子序列中,而且它们的顺序在两个字符串中都一致。
关系式:
1.当元素在矩阵的第一行和第一列时,相应位置的值为0
2.当元素左方和上方的元素相同时,相应位置的值为左上角的值加1
3.当元素左方和上方的元素不相同时,相应位置的值为左方和上方的元素中值较大的那个数的值
变化过程:
矩阵初始化:
关系式计算后的图:
形成过程:
- 得到的最右下角的元素4是最长公共子序列的长度
- 由4往前推可以得到变化路线
- 元素发生变化的位置对应的元素为公共元素
代码:
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
// 计算两个字符串的最长公共子序列
string longestCommonSubsequence(const string& text1, const string& text2) {
int m = text1.size();
int n = text2.size();
// 创建一个二维动态数组,用于存储最长公共子序列长度
//创建了一个 m + 1 行、n + 1 列的二维向量 dp,并且将其中的所有元素初始化为 0
vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
// 动态规划计算最长公共子序列长度
for (int i = 1; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
if (text1[i - 1] == text2[j - 1]) {
// 如果当前字符相同,则最长公共子序列长度加一
dp[i][j] = dp[i - 1][j - 1] + 1;
}
else {
// 如果当前字符不同,则取左边或上边的最长公共子序列长度的较大值
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
//根据最长公共子序列的长度构造最长公共子序列。我们从数组的右下角开始,倒序遍历数组,
// 并根据字符是否相同进行移动。如果当前字符相同,将该字符添加到结果字符串中,并向左上方移动;
// 如果上方的最长公共子序列长度较大,则向上方移动;如果左边的最长公共子序列长度较大,则向左边移动。
//最后,返回结果字符串作为最长公共子序列。
// 根据最长公共子序列的长度构造最长公共子序列
int length = dp[m][n];
string result(length, ' ');
int i = m, j = n;
while (i > 0 && j > 0) {
if (text1[i - 1] == text2[j - 1]) {
// 如果当前字符相同,则将该字符加入最长公共子序列,并向左上方移动
result[length - 1] = text1[i - 1];
i--;
j--;
length--;
}
else if (dp[i - 1][j] > dp[i][j - 1]) {
// 如果上方的最长公共子序列长度较大,则向上方移动
i--;
}
else {
// 如果左边的最长公共子序列长度较大,则向左边移动
j--;
}
}
return result;
}
int main() {
string text1 = "abcde";
string text2 = "ace";
string lcs = longestCommonSubsequence(text1, text2);
cout << "Longest Common Subsequence: " << lcs << endl;
return 0;
}