1. 程式人生 > 實用技巧 >97. 交錯字串-7月18日

97. 交錯字串-7月18日

題目

97. 交錯字串

我的思路和實現

我的思路是給s1,s2和s3各設定一個指標,用來指示當前待匹配的字元 遞迴思路解決: 演算法應該沒有邏輯問題,可是會遞迴+回溯時間複雜度較大,達到了2^n級別
class Solution {
public:
    bool result;
    void recursion(string& s1,int i1,string& s2,int i2,string& s3,int i3){
        //printf("s1:%d %c\n",i1,s1[i1]);
        //遞迴終結條件
        if
(i3==s3.size()&&i1==s1.size()&&i2==s2.size()){ result=true;return;} //當前層處理邏輯 if(i1<s1.size()) if(s1[i1]==s3[i3]) recursion(s1,i1+1,s2,i2,s3,i3+1); //遞迴到下一層 if(i2<s2.size()) if(s2[i2]==s3[i3]) recursion(s1,i1,s2,i2+1,s3,i3+1);
//清除當前層 return; } bool isInterleave(string s1, string s2, string s3) { result = false; recursion(s1,0,s2,0,s3,0); return result; } };
本題官方題解的思路是動態規劃: s1的前i個字元和s2的前j個字元是否能匹配s3的前i+j個字元的條件是:
  • s1的前i-1個字元和s2的前j個字元與s3的前i+j-1個字元匹配&&s1的第i個字元與s3的第i+j個字元匹配或者
  • s1的前i個字元和s2的前j-1個字元與s3的前i+j-1個字元匹配&&s2的第j個字元與s3的第i+j個字元匹配

遞推到最後的結果得到s3得起整個字串能否被s1和s2的兩個字串交叉匹配

有點類似與遍歷一個m*n的二維陣列,每個元素的值取決於其左邊和上面相鄰的元素的值,時間複雜度為m*n
class Solution {
public:
    bool isInterleave(string s1, string s2, string s3) {
        auto f = vector <int> (s2.size() + 1, false);

        int n = s1.size(), m = s2.size(), t = s3.size();

        if (n + m != t) {
            return false;
        }

        f[0] = true;
        for (int i = 0; i <= n; ++i) {
            for (int j = 0; j <= m; ++j) {
                int p = i + j - 1;
                if (i > 0) {
                    f[j] &= (s1[i - 1] == s3[p]);
                }
                if (j > 0) {
                    f[j] |= (f[j - 1] && s2[j - 1] == s3[p]);
                }
            }
        }

        return f[m];
    }
};
對比我的沒有記憶化的遞迴方法:
  • 為什麼時間複雜度一個是m*n,一個是2^n?
    • 個人理解是在我的解決方案中,匹配到s1的第i個字元s2的第j字元時,此時程式中是隱式地記錄了具體i+j個字元是怎麼交叉排序的(遞迴函式的呼叫棧);而動態規劃的解決方案中沒有記錄前i+j個字元具體是如何交叉排序的,只記錄了是否前i+j個字元可以匹配!
對我的思路的改進: 記憶化的遞迴,用一個全域性變數二維陣列boolmemo[][]來輔助記憶。memo[i][j]記錄的是s1的前i個字元和s2的前j個字元是否能交叉匹配s3的前i+j個字元。這樣一來大大減少了遞迴函式的呼叫次數,總共最多呼叫m*n次,時間複雜度也降到了m*n。
public:
    bool result;
    bool** memo;
    void recursion(string& s1,int i1,string& s2,int i2,string& s3,int i3){
        //printf("s1:%d %c\n",i1,s1[i1]);
        //遞迴終結條件
        if(memo[i1][i2]==false)return;
        if(i3==s3.size()&&i1==s1.size()&&i2==s2.size()){ result=true;return;}
        //當前層處理邏輯
        if(i1<s1.size())
        if(s1[i1]==s3[i3]) recursion(s1,i1+1,s2,i2,s3,i3+1);
        //遞迴到下一層
        if(i2<s2.size())
        if(s2[i2]==s3[i3]) recursion(s1,i1,s2,i2+1,s3,i3+1);
        //清除當前層
        memo[i1][i2]=false;
        return;
    }
    bool isInterleave(string s1, string s2, string s3) {
        result = false;
        memo = new bool*[s1.size()+1];
        for(int i=0;i<s1.size()+1;i++){
        memo[i] = new bool[s2.size()+1];
        for(int j=0;j<s2.size()+1;j++){
            memo[i][j]=true;
        }}
        recursion(s1,0,s2,0,s3,0);
        return result;
    }
};

拓展學習

C++宣告二維陣列

轉自:C++宣告二維陣列

#include <iostream>
#include <vector>
 
using namespace std;
int rows=2,columns=3;

使用一維陣列模型二維陣列

int a0[] = {1,2,3,4,5,6};
    for(int i=0;i<rows;i++){
        for(int j=0;j<columns;j++){
            cout<<a0[i*columns+j]<<" ";//a0[i*columns+j]等價於a0[i][j]
        }
        cout<<endl;
    }

靜態二維陣列

int a1[2][3] = {1,2,3,4,5,6};

動態二維陣列!要熟練

    //申請空間
    int** a2 = new int*[rows];
    for(int i=0;i<rows;i++)
        a2[i] = new int[columns];
    //釋放空間
    for(int i=0;i<rows;i++)
        delete []a2[i];
    delete []a2;

利用vector建立二維陣列

    vector<vector<int> > a3(rows,vector<int>(columns));
    for(int i=0;i<rows;i++){//初始化
         for(int j=0;j<columns;j++){
            a3[i][j] = a1[i][j];
         }
    }