leetcode 1092. 最短公共超序列

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

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

示例:

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

容易想到最终的方案必然是由三部分组成:两字符串的公共子序列(且必然是最长公共子序列)+ 两者特有的字符部分。可以先求最长公共子序列的dp,其中dp[i][j]为考虑s1前i个字符和s2前j个字符的最长公共子序列的长度,然后回溯法一一还原:

使用 i 变量指向 s1 的尾部m, 使用 j 变量指向 s2 的尾部n, f[i][j] 从何值转移而来:

若 i 或 j 其一走完(i = 0 或 j = 0),将剩余字符追加到答案中;
当 f[i][j]=f[i−1][j−1]+1 且 s1[i-1]=s2[j-1] 时,此时它们为 LCS 中的字符,将其追加到具体方案,并让 i 和 j 同时前移;
当 f[i][j]=f[i−1][j],s1[i-1]为特有字符,将 s1[i-1] 追加到答案中,令 i 前移;
当 f[i][j]=f[i][j-1],s2[j-1]为特有字符,将 s2[j-1] 追加到答案中,令 j 前移;
当 f[i][j]=f[i-1][j-1],s1[i-1]或s2[j-1] 随便一个追加到答案中,令i j同时前移;

最后,由于我们是从后往前进行构造,在返回时需要再进行一次翻转。

时间复杂度空间复杂度都是O(mn)

class Solution:
    def shortestCommonSupersequence(self, str1: str, str2: str) -> str:
        m = len(str1)
        n = len(str2)
        dp = [[0]*(n+1) for _ in range(m+1)]
        for i in range(1, m+1):
            for j in range(1, n+1):
                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])
        
        res = ''
        i = m
        j = n 
        while (i > 0) and (j > 0):
            if (dp[i][j] == dp[i-1][j-1] + 1) and (str1[i-1] == str2[j-1]):
                res += str1[i-1]
                i -= 1
                j -= 1
            elif dp[i][j] == dp[i-1][j]:
                res += str1[i-1]
                i -= 1
            elif dp[i][j] == dp[i][j-1]:
                res += str2[j-1]
                j -= 1
            elif dp[i][j] == dp[i-1][j-1]:
                res += str1[i-1]
                i -= 1 
                j -=1 
        if i == 0:
            res += str2[:j][::-1]
        if j == 0:
            res += str1[:i][::-1]
         
        return res[::-1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值