难度:中等。
标签:字符串,动态规划。
回溯
开始使用回溯法直接算,第99/106个用例超时了。超时用例:
"bbbbbabbbbabaababaaaabbababbaaabbabbaaabaaaaababbbababbbbbabbbbababbabaabababbbaabababababbbaaababaa"
"babaaaabbababbbabbbbaabaabbaabbbbaabaaabaababaaaabaaabbaaabaaaabaabaabbbbbbbbbbbabaaabbababbabbabaab"
"babbbabbbaaabbababbbbababaabbabaabaaabbbbabbbaaabbbaaaaabbbbaabbaaabababbaaaaaabababbababaababbababbbababbbbaaaabaabbabbaaaaabbabbaaaabbbaabaaabaababaababbaaabbbbbabbbbaabbabaabbbbabaaabbababbabbabbab"
dfs的参数choose表示当前要从第1/2个字符中选择。
超时解法:
class Solution {
bool dfs(string s1, string s2, string s3, int k1, int k2, int k3, int choose){
if(k1 == s1.length() && k2 == s2.length() && k3 == s3.length()){
return true;
}
if(choose == 1 && k1 == s1.length())return false;
if(choose == 2 && k2 == s2.length())return false;
bool res = false;
if(choose == 1 && s1[k1] == s3[k3]){
while(s1[k1] == s3[k3]){
k1++;
k3++;
if(dfs(s1, s2, s3, k1, k2, k3, 2)){
res = true;
break;
}
}
}
else if(choose == 2 && s2[k2] == s3[k3]){
while(s2[k2] == s3[k3]){
k2++;
k3++;
if(dfs(s1, s2, s3, k1, k2, k3, 1)){
res = true;
break;
}
}
}
return res;
}
public:
bool isInterleave(string s1, string s2, string s3) {
int n1 = s1.length(), n2 = s2.length(), n3 = s3.length();
if(n1 + n2 != n3)return false;
return dfs(s1, s2, s3, 0, 0, 0, 1) || dfs(s1, s2, s3, 0, 0, 0, 2);
}
};
慢慢优化:
- 将dfs中的k3去掉,k3可以表示为k1+k2,至此dfs变为k1,k2和choose的函数。
- 使用两个二维矩阵dp1,dp2来存储dfs的结果。
然后就得到了一个非常简单的dfs+动态存储的代码,提交后通过,只是效率仍然很低。
正确解法:
class Solution {
vector<vector<int>> dp1, dp2;
bool dfs(string s1, string s2, string s3, int k1, int k2, int choose){
if(k1 == s1.length() && k2 == s2.length() && k1 + k2 == s3.length()){
return true;
}
if(choose == 1 && k1 == s1.length())return false;
if(choose == 2 && k2 == s2.length())return false;
bool res = false;
int k3 = k1 + k2;
if(choose == 1 && s1[k1] == s3[k3]){
while(s1[k1] == s3[k3]){
k1++;
k3++;
if(dp2[k1][k2] == -1)dp2[k1][k2] = dfs(s1, s2, s3, k1, k2, 2);
if(dp2[k1][k2]){
res = true;
break;
}
}
}
else if(choose == 2 && s2[k2] == s3[k3]){
while(s2[k2] == s3[k3]){
k2++;
k3++;
if(dp1[k1][k2] == -1)dp1[k1][k2] = dfs(s1, s2, s3, k1, k2, 1);
if(dp1[k1][k2]){
res = true;
break;
}
}
}
return res;
}
public:
bool isInterleave(string s1, string s2, string s3) {
int n1 = s1.length(), n2 = s2.length(), n3 = s3.length();
if(n1 + n2 != n3)return false;
dp1.resize(n1 + 1);
dp2.resize(n1 + 1);
for(int i = 0; i <= n1; ++i){
dp1[i].resize(n2 + 1, -1);
dp2[i].resize(n2 + 1, -1);
}
return dfs(s1, s2, s3, 0, 0, 1) || dfs(s1, s2, s3, 0, 0, 2);
}
};
结果:
动态规划
继续优化,去掉深搜,直接改为动态规划来做。
想了想,其实根本不需要记当前的选择是哪个,不是1就是2,而且每次选择的字符个数是不定的,因此这个题就是判断将s1和s2按顺序交叉拼接,是否能构成s3。
使用一个二维数组来记录,二维数组长度分别为n1+1, n2+1。
开始我就是被题目中强调的顺序条件迷惑了。
正确解法:
class Solution {
public:
bool isInterleave(string s1, string s2, string s3) {
int n1 = s1.length(), n2 = s2.length(), n3 = s3.length();
if(n1 + n2 != n3)return false;
vector<vector<int>> dp(n1 + 1, vector<int>(n2 + 1, 0));
dp[0][0] = 1;
for(int i = 1; i <= n1; ++i){
if(s1[i - 1] == s3[i - 1] && dp[i - 1][0])dp[i][0] = 1;
}
for(int i = 1; i <= n2; ++i){
if(s2[i - 1] == s3[i - 1] && dp[0][i - 1])dp[0][i] = 1;
}
for(int i = 1; i <= n1; ++i){
for(int j = 1; j <= n2; ++j){
int k = i + j - 1;
if(s1[i - 1] == s3[k] && dp[i - 1][j]){
dp[i][j] = 1;
}
if(s2[j - 1] == s3[k] && dp[i][j - 1]){
dp[i][j] = 1;
}
}
}
return dp[n1][n2];
}
};
结果: