题目链接
Leetcode.1092 最短公共超序列 Rating : 1977
题目描述
给出两个字符串 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”。
最终我们给出的答案是满足上述属性的最短字符串。
提示:
- 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
str1
和str2
都由小写英文字母组成。
解法:记忆化搜索 + 递归
我们定义
d
f
s
(
i
,
j
)
dfs(i,j)
dfs(i,j) 为 s
的前 i
个字符,t
的前 j
个字符构成的最短字符串的长度。
我们从 s
和 t
各自的最后一位字符开始匹配。
- 当
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(i−1,j−1)+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,j−1),dfs(i,j−1))+1。
我们定义
d
f
s
1
(
i
,
j
)
dfs1(i,j)
dfs1(i,j) 为 s
的前 i
个字符,t
的前 j
个字符构成的最短字符串。
我们从 s
和 t
各自的最后一位字符开始匹配。
- 当
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(i−1,j−1)+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(i−1,j)+1,说明
s[i]
在答案当中,返回 d f s 1 ( i − 1 , j ) + s [ i ] dfs1(i-1,j) + s[i] dfs1(i−1,j)+s[i]
- 如果
d
f
s
(
i
,
j
)
=
d
f
s
(
i
−
1
,
j
)
+
1
dfs(i,j) = dfs(i - 1,j) + 1
dfs(i,j)=dfs(i−1,j)+1,说明
-
- 否则 ,说明
t[j]
在答案当中,返回 d f s 1 ( i , j − 1 ) + t [ j ] dfs1(i,j-1) + t[j] dfs1(i,j−1)+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);
}
};