leetcode-Interleaving String

Given s1s2s3, 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];
    }
    
};



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章