1092. 最短公共超序列
题目链接:1092. 最短公共超序列
方法一:转换为lcs(最长公共子序列)问题进行求解
解题思路
先得到两个字符串\(lcs\),然后再计算\(ans\)。
- \(lcs\)的获取:最长公共子序列 编辑距离【基础算法精讲 19】
动态规划的\(dp\)数组在本题有两种实现方式
(1)vector<vector<string>> dp(n + 1, vector<string>(m + 1));,\(dp[i][j]\)表示\(str1[0, i]\)和\(str2[0, j]\)的最长公共子序列,\(lcs = dp[n][m]\)。
(2)vector<vector<int>> dp(n + 1, vector<int>(m + 1));,\(dp[i][j]\)表示\(str1[0, i]\)和\(str2[0, j]\)的最长公共子序列的长度,然后再回溯\(dp\)数组的值得到\(lcs\)。
// 方式一
for (int i = 1; i <= n; i ++ ) {
for (int j = 1; j <= m; j ++ ) {
if (str1[i - 1] == str2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + str1[i - 1];
} else {
if (dp[i][j - 1].length() > dp[i - 1][j].length()) {
dp[i][j] += dp[i][j - 1];
} else {
dp[i][j] += dp[i - 1][j];
}
}
}
}
string lcs = dp[n][m];
// 方式二
for (int i = 1; i <= n; i ++ ) {
for (int j = 1; j <= m; j ++ ) {
if (str1[i - 1] == str2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
string lcs;
int i = n, j = m;
while (dp[i][j]) {
if (dp[i - 1][j] == dp[i][j]) {
i -- ;
} else if (dp[i][j - 1] == dp[i][j]) {
j -- ;
} else {
lcs = str1[i - 1] + lcs;
i -- ;
j -- ;
}
}
- \(ans\)计算
本题要求\(ans\)包含子序列\(str1\)和\(str2\),且要求\(ans\)最短,那么\(ans\)只需要包含两个字符串的\(lcs\)以及其各自不在\(lcs\)中的字符即可,并且保证各自在\(ans\)中两个字符串的字符相对位置保持不变。
string ans;
int idx = lcs.length(), k = 0;
i = 0, j = 0;
while (k < idx) {
while (str1[i] != lcs[k]) { // 将str1中lcs[k]之前的字符加入ans
ans += str1[i];
i ++ ;
}
while (str2[j] != lcs[k]) { // 将str2中lcs[k]之前的字符加入ans
ans += str2[j];
j ++ ;
}
ans += lcs[k]; // 等两个字符串中在lcs[k]之前的字符加入后,再将lcs[k]加入ans中,然后重复上述操作
k ++ ;
i ++ ;
j ++ ;
}
while (i < n) ans += str1[i ++ ];
while (j < m) ans += str2[j ++ ];
代码
class Solution {
public:
string shortestCommonSupersequence(string str1, string str2) {
// 计算str1和str2的lcs的dp数组
int n = str1.length(), m = str2.length();
vector<vector<int>> dp(n + 1, vector<int>(m + 1));
for (int i = 1; i <= n; i ++ ) {
for (int j = 1; j <= m; j ++ ) {
if (str1[i - 1] == str2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
// 根据dp数组得出lcs
string lcs;
int i = n, j = m;
while (dp[i][j]) {
if (dp[i - 1][j] == dp[i][j]) {
i -- ;
} else if (dp[i][j - 1] == dp[i][j]) {
j -- ;
} else {
lcs = str1[i - 1] + lcs;
i -- ;
j -- ;
}
}
// 根据lcs得出答案
string ans;
int idx = lcs.length(), k = 0;
i = 0, j = 0;
while (k < idx) {
while (str1[i] != lcs[k]) {
ans += str1[i];
i ++ ;
}
while (str2[j] != lcs[k]) {
ans += str2[j];
j ++ ;
}
ans += lcs[k];
k ++ ;
i ++ ;
j ++ ;
}
while (i < n) ans += str1[i ++ ];
while (j < m) ans += str2[j ++ ];
return ans;
}
};
复杂度分析
时间复杂度:\(O(nm)\);
空间复杂度:\(O(nm)\)。
方法二:动态规划
解题思路
设\(dp[n + 1][m + 1]\),\(dp[i][j]\)表示\(str1[0, i)\)和\(str2[0, j)\)的最短公共超序列的长度。
对于\(dp[i][j]\):
- \(str1[i - 1] == str2[j - 1]\),则\(dp[i][j] = dp[i - 1][j - 1] + 1\);
- 否则,\(dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + 1\)。
然后再回溯求\(str1[0, n + 1) 和 str2[0, m + 1)\)的最短公共超序列。
代码
class Solution {
public:
string shortestCommonSupersequence(string str1, string str2) {
int n = str1.length(), m = str2.length();
vector<vector<int>> dp(n + 1, vector<int>(m + 1));
for (int i = 1; i <= n; i ++ ) dp[i][0] = i;
for (int j = 1; j <= m; j ++ ) dp[0][j] = j;
for (int i = 1; i <= n; i ++ ) {
for (int j = 1; j <= m; j ++ ) {
if (str1[i - 1] == str2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + 1;
}
}
}
string ans;
while (dp[n][m]) {
if (n - 1 >= 0 && m - 1 >= 0 && str1[n - 1] == str2[m - 1] && dp[n][m] == dp[n - 1][m - 1] + 1) {
ans = str1[n - 1] + ans;
n -- ;
m -- ;
} else if (n - 1 >= 0 && dp[n][m] == dp[n - 1][m] + 1) {
ans = str1[n - 1] + ans;
n -- ;
} else if (m - 1 >= 0 && dp[n][m] == dp[n][m - 1] + 1){
ans = str2[m - 1] + ans;
m -- ;
}
}
return ans;
}
};
复杂度分析
时间复杂度:\(O(nm)\);
空间复杂度:\(O(nm)\)。

浙公网安备 33010602011771号