【1092. 最短公共超序列】

该问题是一个编程挑战,涉及动态规划算法来解决。给定两个字符串str1和str2,目标是找到一个最短的字符串,这个字符串同时包含str1和str2作为子序列。动态规划的状态定义为dp[i][j],表示str1[i:]和str2[j:]的最短公共超序列的长度。通过比较字符和选择最优路径,最终构造出结果字符串。
摘要由CSDN通过智能技术生成

来源:力扣(LeetCode)

描述:

给出两个字符串 str1str2,返回同时以 str1str2 作为子序列的最短字符串。如果答案不止一个,则可以返回满足条件的任意一个答案。

(如果从字符串 T 中删除一些字符(也可能不删除,并且选出的这些字符可以位于 T 中的 任意位置),可以得到字符串 S,那么 S 就是 T 的子序列)

示例:

输入:str1 = "abac", str2 = "cab"
输出:"cabac"
解释:
str1 = "abac""cabac" 的一个子串,因为我们可以删去 "cabac" 的第一个 "c"得到 "abac"。 
str2 = "cab""cabac" 的一个子串,因为我们可以删去 "cabac" 末尾的 "ac" 得到 "cab"。
最终我们给出的答案是满足上述属性的最短字符串。

提示:

  • 1 <= str1.length, str2.length <= 1000
  • str1 和 str2 都由小写英文字母组成。

方法:动态规划

思路与算法

首先题目给出两个字符串 str1 和 str2 ,我们需要返回任意一个满足同时以 str1 和 str2 作为子序列的最短字符串。我们设 str1 的长度为 n,str2 的长度为 m, dp[i][j] 表示同时以字符串 str1[i:n] 和 str2[j:m] 作为子序列的最短字符串长度,其中 str1[i:j],str2[i : j] 表示字符串 str1,str2 从下标 i 到下标 j 的子串(包含下标 i 且不包含下标 j)。现在我们来思考如何求解各个状态:

  1. 当 str1[i] = str2[j]时,以 str1[i : n] 和 str2[j : m] 作为子序列的最短字符串的开头字符为 str1 [i] 一定是最优的:
dp[i][j] = dp[i + 1][j+ 1] + 1
  1. 否则当 str1[i] ≠ str2[j] 时,以 str1[i : n] 和 str2[j : m] 作为子序列的最短字符串的开头字符只能为 str1[i] 或者 str2[j]:
dp[i][j] = min{dp[i + 1][j], dp[i][j + 1]} + 1

上文讨论的是建立在 i < n 和 j < m 的前提上的,我们还需要考虑动态规划的边界条件,当 i = n 或 j = m 时,此时其中一个字符串为空串,同时满足以两字符串作为子序列的最短字符串长度为另一个字符串的长度。因此我们就可以写出动态规划的边界条件:

0

可以看到每一个区间上的求解都与其子区间的求解有关,所以我们可以采用「自底向上」的编码方式来实现求解过程。求解完毕后,此时 dp[0][0] 即为此时题目中满足同时以 str1 和 str2 作为子序列的最短字符串长度,然后我们可以通过双指针和通过动态规划状态来从前往后构造出具体方案。我们用指针 t1 = 0 来指向 str1 的头部,用 t2 = 0 来指向 str2 的头部,并根据 dp[i][j] 来从前往后构造出具体字符串方案:

  1. 若 t1 到达 str1 尾部(t1 = n)或者 t2 到达 str2 尾部(t2 = m),将对应的剩下的字符加到结果字符串后面。
  2. 否则:
    • 若 str1[t1] = str2[t2],则此时结果字符串最后添加 str1[t1] 为最优条件,然后 t1
      和 t2 指针分别往后移动一单位。
    • 否则若当

1

向结果字符串中添加 str1 [t1],然后 t1 指针往后移动一单位。若当

2

则向结果字符串中添加 str2[t2],然后 t2 指针往后移动一单位。由于我们只需要返回任一符合条件的结果字符串,所以若条件 (1)(2) 都满足我们只需要取其中一种情况即可。

代码:

class Solution {
public:
    string shortestCommonSupersequence(string str1, string str2) {
        int n = str1.size(), m = str2.size();
        vector<vector<int>> dp(n + 1, vector<int>(m + 1));
        for (int i = 0; i < n; ++i) {
            dp[i][m] = n - i;
        }
        for (int i = 0; i < m; ++i) {
            dp[n][i] = m - i;
        }
        for (int i = n - 1; i >= 0; --i) {
            for (int j = m - 1; j >= 0; --j) {
                if (str1[i] == str2[j]) {
                    dp[i][j] = dp[i + 1][j + 1] + 1;
                } else {
                    dp[i][j] = min(dp[i + 1][j], dp[i][j + 1]) + 1;
                }
            }
        }
        string res;
        int t1 = 0, t2 = 0;
        while (t1 < n && t2 < m) {
            if (str1[t1] == str2[t2]) {
                res += str1[t1];
                ++t1;
                ++t2;
            } else if (dp[t1 + 1][t2] == dp[t1][t2] - 1) {
                res += str1[t1];
                ++t1;
            } else if (dp[t1][t2 + 1] == dp[t1][t2] - 1) {
                res += str2[t2];
                ++t2;
            }
        }
        if (t1 < n) {
            res += str1.substr(t1);
        }
        else if (t2 < m) {
            res += str2.substr(t2);
        }
        return res;
    }
};

执行用时:20 ms, 在所有 C++ 提交中击败了75.29%的用户
内存消耗:12.5 MB, 在所有 C++ 提交中击败了80.59%的用户
复杂度分析
时间复杂度:O(n×m)。其中预处理动态规划求解的复杂度为 O(n×m),构造具体字符串方案的复杂度为 O(n+m),其中 n 为字符串 str1 的长度,m 为字符串 str2 的长度。
空间复杂度:O(n×m),其中 n 为字符串 str1 的长度,m 为字符串 str2 的长度。空间复杂度主要取决于动态规划模型中状态的总数。
author:LeetCode-Solution

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千北@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值