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.
這道題需要判斷兩個字符串能否合併組成另外一個新的字符串,這個合併是有序的合併,就是在新的字符串中,可以分別找到跟原來兩個字符串,並且字母的順序跟原兩個字符串字母順序是一致的。
假設s1長度爲n1, s2長度爲n2,那麼s3的每個字母都有2種選擇,即s3的第一個字母可以從s1開頭取,也可以從s2開頭取,這樣一共要從s1和s2中取(n1+n2)個數,總共s3可能數爲從n1+n2中取n1個.
這裏是要判斷s3是否可以由s1和s2有序合併。容易想到用遞歸來求解,比較s3的第一個字母與s1和s2的第一個字母,如果s3[0]=s1[0],說明s3的第一個數可以從s1取,這樣要繼續搜索s3後面的字符串能否用s1的第一個字母后的字符串和s2拼成;如果s3[0]=s2[0],說明s3的第一個數也可以從s2取,然後繼續搜索s3後面字符串能否用s1和s2第一個字母后字符串拼成。這樣遞歸下去是可以算出答案的。
所以編個簡單的遞歸如下:
class Solution {
public:
bool isInterleave(string s1, string s2, string s3) {
if (s3.size() != s1.size()+s2.size())
return false;
return isInterleave(s1, 0, s2, 0, s3, 0);
}
private:
bool isInterleave(const string &s1, int b1, const string &s2, int b2,
const string& s3, int b3) {
if (b1 == s1.size()) // s1 reaches end
return s3.substr(b3) == s2.substr(b2);
if (b2 == s2.size()) // s2 reaches end
return s3.substr(b3) == s1.substr(b1);
if (s3[b3] == s1[b1]) { //match s1
if (isInterleave(s1, b1+1, s2, b2, s3, b3+1))
return true;
}
if (s3[b3] == s2[b2]) { //match s2
if (isInterleave(s1, b1, s2, b2+1, s3, b3+1))
return true;
}
return false; // no match
}
};
最終結果是TLE。因爲上面的遞歸計算了很多重複的子任務,所以改成記憶化搜索如下:class Solution {
public:
bool isInterleave(string s1, string s2, string s3) {
if (s3.size() != s1.size()+s2.size())
return false;
memset(hasSearched, false, sizeof(hasSearched));
return isInterleave(s1, 0, s2, 0, s3, 0);
}
private:
bool isInterleave(const string &s1, int b1, const string &s2, int b2,
const string& s3, int b3) {
if (b1 == s1.size()) // s1 reaches end
return s3.substr(b3) == s2.substr(b2);
if (b2 == s2.size()) // s2 reaches end
return s3.substr(b3) == s1.substr(b1);
if (s3[b3] == s1[b1]) { //match s1
if (!hasSearched[b1+1][b2] && isInterleave(s1, b1+1, s2, b2, s3, b3+1))
return true;
}
if (!hasSearched[b1][b2+1] && s3[b3] == s2[b2]) { //match s2
if (isInterleave(s1, b1, s2, b2+1, s3, b3+1))
return true;
}
hasSearched[b1][b2] = true;
return false; // no match
}
bool hasSearched[100][100];
};
也還可以再繼續優化,比如檢測到s3字符與s1字符相等時,可以先行判斷s3後面的一個字符是否與s1、s2的前面字符相等,如果不等,這條路徑就不必走下去了。
其實也可以用動態規劃求解。
class Solution {
public:
bool isInterleave(string s1, string s2, string s3) {
if (s3.size() != s1.size()+s2.size())
return false;
bool d[200][200] = {false}; // d[i][j]表示s1的前i個字符和s2的前j個字符能否組成s3的前i+j個字符
d[0][0] = true;
int len1=s1.size(), len2=s2.size();
for (int i=0; i<=len1; ++i) {
for (int j=0; j<=len2; j++) {
if (s1[i]==s3[i+j] && d[i][j]==true)
d[i+1][j] = true;
if (s2[j]==s3[i+j] && d[i][j]==true)
d[i][j+1] = true;
}
}
return d[len1][len2];
}
};