Leetcode.1092 最短公共超序列

题目链接

Leetcode.1092 最短公共超序列 Rating : 1977

题目描述

给出两个字符串 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 < = s t r 1. l e n g t h , s t r 2. l e n g t h < = 1000 1 <= str1.length, str2.length <= 1000 1<=str1.length,str2.length<=1000
  • str1str2都由小写英文字母组成。

解法:记忆化搜索 + 递归

我们定义 d f s ( i , j ) dfs(i,j) dfs(i,j)s的前 i个字符,t的前 j个字符构成的最短字符串的长度。

我们从 st各自的最后一位字符开始匹配。

  • s [ i ] = t [ j ] s[i] = t[j] s[i]=t[j]时,s[i]t[j]必定是在答案中的,所以我们缩减问题的规模,继续讨论[i-1,j-1],返回 d f s ( i − 1 , j − 1 ) + 1 dfs(i-1,j-1) + 1 dfs(i1,j1)+1
  • s [ i ] ≠ t [ j ] s[i] \neq t[j] s[i]=t[j]时,s[i]t[j]只有其中一个会出现在答案中,我们分开讨论,只要长度小的那个。分别讨论 [i-1,j][i,j-1],返回 m i n ( d f s ( i , j − 1 ) , d f s ( i , j − 1 ) ) + 1 min(dfs(i,j-1) , dfs(i , j - 1) ) + 1 min(dfs(i,j1),dfs(i,j1))+1

我们定义 d f s 1 ( i , j ) dfs1(i,j) dfs1(i,j)s的前 i个字符,t的前 j个字符构成的最短字符串。

我们从 st各自的最后一位字符开始匹配。

  • s [ i ] = t [ j ] s[i] = t[j] s[i]=t[j]时,s[i]t[j]必定是在答案中的,所以我们缩减问题的规模,继续讨论[i-1,j-1],返回 d f s 1 ( i − 1 , j − 1 ) + s [ i ] dfs1(i-1,j-1) + s[i] dfs1(i1,j1)+s[i]
  • s [ i ] ≠ t [ j ] s[i] \neq t[j] s[i]=t[j]时,s[i]t[j]只有其中一个会出现在答案中,我们分开讨论。
    • 如果 d f s ( i , j ) = d f s ( i − 1 , j ) + 1 dfs(i,j) = dfs(i - 1,j) + 1 dfs(i,j)=dfs(i1,j)+1,说明s[i]在答案当中,返回 d f s 1 ( i − 1 , j ) + s [ i ] dfs1(i-1,j) + s[i] dfs1(i1,j)+s[i]
    • 否则 ,说明t[j]在答案当中,返回 d f s 1 ( i , j − 1 ) + t [ j ] dfs1(i,j-1) + t[j] dfs1(i,j1)+t[j]

时间复杂度: O ( m n ) O(mn) O(mn)

C++代码:

class Solution {
public:
    string shortestCommonSupersequence(string &s, string &t) {
        int m = s.size(), n = t.size();
        vector<vector<int>> st(m, vector<int>(n, -1));

        function<int(int, int)> dfs = [&](int i, int j) -> int {
            if (i < 0) return j + 1; // s 是空串,返回剩余的 t 的长度
            if (j < 0) return i + 1; // t 是空串,返回剩余的 s 的长度
            int &res = st[i][j];
            if (res != -1) return res; // 避免重复计算 dfs 的结果
            if (s[i] == t[j]) // 最短公共超序列一定包含 s[i]
                return res = dfs(i - 1, j - 1) + 1;
            return res = min(dfs(i - 1, j), dfs(i, j - 1)) + 1;
        };

        function<string(int, int)> make_ans = [&](int i, int j) -> string {
            if (i < 0) return t.substr(0, j + 1); // s 是空串,返回剩余的 t
            if (j < 0) return s.substr(0, i + 1); // t 是空串,返回剩余的 s
            if (s[i] == t[j]) // 最短公共超序列一定包含 s[i]
                return make_ans(i - 1, j - 1) + s[i];
            if (dfs(i, j) == dfs(i - 1, j) + 1)
                return make_ans(i - 1, j) + s[i];
            return make_ans(i, j - 1) + t[j];
        };

        return make_ans(m - 1, n - 1);
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值