给出两个字符串 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] + 1else:dp[i][j] = max(dp[i-1][j], dp[i][j-1])res = ''i = mj = 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 -= 1j -= 1elif dp[i][j] == dp[i-1][j]:res += str1[i-1]i -= 1elif dp[i][j] == dp[i][j-1]:res += str2[j-1]j -= 1elif 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]
上一篇:样式声明——列表属性
下一篇:UART驱动情景分析-注册