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"
, return true.
When s3 = "aadbbbaccc"
, return false.
分析:简单暴力流DFS,效率不过。需要考虑其他方法
方法一:DFS居然没有超时, 你敢信。而且如果我不加个dp表,它还会进入“死循环”,这一点开始不能理解。
class Solution {
bool ret;
int dp[1000][1000];
public:
bool isInterleave(string s1, string s2, string s3) {
if(s1.size() + s2.size() != s3.size()) return false;
DFS(s1, s2, s3, 0, 0, 0);
return ret;
}
void DFS(string &s1, string&s2, string &s3, int i, int j, int k) {
if(ret || dp[i][j]) return;
dp[i][j] = true;
if(k == s3.size()) {
ret = true;
return ;
}
if(i < s1.size() && s1[i] == s3[k]) DFS(s1, s2, s3, i + 1, j, k + 1);
if(j < s2.size() && s2[j] == s3[k]) DFS(s1, s2, s3, i, j + 1, k + 1);
}
};
死循环的原因在于下面这张dp[i][j]表:
这里每次都更新了dp[i][j], dp[i][j] 表示s1[i] 和 s2[j] 准备和 s3[i + j]比较的情况次数。在DFS过程中,若dp[i][j] 表示一种状态,那么到另外一种状态可能有很多方式,再到下一种状态又有很多方式,类似一颗树的结构。而状态数量的增长应该和指数增长速度相当(定性分析),这样某种状态会重复出现,出现“死循环”的假象。
简单数据证明:
string s1 = "bbbbbabbbbabaababaaaabbababbaaabbabbaaabaaaaababbbababbbbbabbbbababbabaabababbbaabababababbbaaababaa";
string s2 = "babaaaabbababbbabbbbaabaabbaabbbbaabaaabaababaaaabaaabbaaabaaaabaabaabbbbbbbbbbbabaaabbababbabbabaab";
string s3 = "babbbabbbaaabbababbbbababaabbabaabaaabbbbabbbaaabbbaaaaabbbbaabbaaabababbaaaaaabababbababaababbababbbababbbbaaaabaabbabbaaaaabbabbaaaabbbaabaaabaababaababbaaabbbbbabbbbaabbabaabbbbabaaabbababbabbabbab";
上图中最右下角的那个数字2,为dp[45][32] = 2,表示函数递归进入i = 45、j = 32两次。若不加dp表,让函数递归每次打印 i, j, k, 会看到 i = 45、j = 32、k = 77这个数据一直重复出现。这也就是上面解释的“死循环”的假象, 原因是某些状态重复出现,状态转移导致规模指数增长。
方法二:迭代dp,感觉跟二位地图测试是否可以从左上角可达右下角问题类似,不过要注意边界。
设dp[i][j] 表示取 i 个s1元素和 j 个s2 元素,是否能与 s3匹配。这样就可以建立dp[i][j]、dp[i][j-1]、dp[i-1][j] 之间的关系了
class Solution {
int dp[1000][1000];
public:
bool isInterleave(string s1, string s2, string s3) {
int len1 = s1.size();
int len2 = s2.size();
int len3 = s3.size();
if(len1 + len2 != len3) return false;
dp[0][0] = true;
for(int i = 0; i < len1; i++) {
if(s1[i] == s3[i]) dp[i + 1][0] = true;
else break;
}
for(int j = 0; j < len2; j++) {
if(s2[j] == s3[j]) dp[0][j + 1] = true;
else break;
}
for(int i = 1; i <= len1; i++) {
for(int j = 1; j <= len2; j++) {
if(s1[i - 1] == s3[i+j - 1] && dp[i-1][j]) {
dp[i][j] = true;
} else if(s2[j - 1] == s3[i+j - 1] && dp[i][j-1]) {
dp[i][j] = true;
}
}
}
return dp[len1][len2];
}
};
时间复杂度与方法一是一样的 O(len1 * len2) , 题目跟 LeetCode OJ - Minimum Path Sum 异曲同工