LeetCode 97. Interleaving String 题解
题目描述
Given s1, s2, s3, find whether s3 is formed by the interleaving of s1 and s2.
For example,
Given:
s1 = "aabcc"
,
s2 = "dbbca"
,When
s3 = "aadbbcbcac"
, returntrue
.
Whens3 = "aadbbbaccc"
, returnfalse
.
题目大意
这道题是询问, 对于两个字符串s1, s2
能否在不改变原字符串中字符出现顺序的情况下, 通过s1,s2
字符的交替重组, 得到字符串s3
.
思路分析
不难发现, 这道题和DP经典问题LCS之类很有相似之处。几乎可以马上做出采用DP的决定。并且复杂度为
O(n2)
.关键在于转移方程。
转移方程即状态转移。下面以
ans[i,j]
标识状态。 根据之前一些题目的经验, 可以马上推断出上一个状态为
ans[i−1,j]和ans[i,j−1]
。 其中
ans[i,j]
表示当s1
的前i
个字符与s2
的前j
个字符能够构成的s3
的最长前缀长度。
明显的, 最后的判断即看 ans[s1.size()][s2.size()] 是否等于 s3.size()
有一个很不顺的地方,在于初状态的构建。这题比较特别, 和LCS有较大的不同。
首先如果按照之前的初始化方法。 一般是构建状态数组的第一行与第一列。这里需要注意的是, 第一行与第一列,分别代表的意义是:当s1为空的时候, 以及s2 为空的时候的状态。
而普通的情况通常是, 第一行第一列是代表分别两个序列的第一个元素时的状态
之所以这样变,是考虑到如果不增加一个“空状态” 状态转移会变的突兀。 因为这时候可能会突然增加2个字符。原先的状态转移方程就不成立了。
下面是具体的代码。 是比较久之前写的了。 现在来看貌似仍有改进的余地:
class Solution {
public:
bool isInterleave(string s1, string s2, string s3) {
if (s3.size() != s1.size() + s2.size()) return false;
vector<vector<int> > ans(s1.size() + 1);
for (int i = 0; i < ans.size(); ++i) {
ans[i].resize(s2.size() +1);
}
for (int i = 1; i <= s1.size(); ++i) {
if (s3[i - 1] == s1[i - 1]) ans[i][0] = i;
else break;
}
for (int j = 1; j <= s2.size(); ++j) {
if (s3[j - 1] == s2[j - 1]) ans[0][j] = j;
else break;
}
for (int i = 1; i <= s1.size(); ++i) {
for (int j = 1; j <= s2.size(); ++j) {
if (s2[j - 1] == s3[ans[i][j - 1]]) {
ans[i][j] = max(ans[i][j], ans[i][j - 1] + 1);
}
if (s1[i - 1] == s3[ans[i - 1][j]]) {
ans[i][j] = max(ans[i][j], ans[i - 1][j] + 1);
}
}
}
return (ans[s1.size()][s2.size()] == s3.size());
}
};
其他细节
- 上面代码的时间复杂度已经达到 O(n2) 但是空间复杂度还有优化的余地。 判断空间复杂度是否可以优化只需要看当前状态是从哪些上一状态转移过来的。如果都只相邻的状态。 那么早先计算的大部分状态将不会被再次用到。 那么就进行了状态的压缩。
- 这道题的初始状态比较特殊。 自己要引起一些注意。