Leetcode中两个字符串匹配

字符串的匹配问题很多得依靠DP来解答,在做题的过程中,虽然能用递归完成,但是大都以TLE结束。后来分析了下,主要还是枚举的分支太多,造成枚举负担太大。


题目一:Interleaving String

Given s1, s2, s3, find whether s3 is formed by  the iterleaving of s1 and s2. 

思路1:这个题如果 用递归的方法解决,得需要用下标分别控制s1, s2, s3, 就像Merge一样。另外,由于这是一道判断是非题,该递归还需要返回值。

public class Solution {
    public boolean isInterleave(String s1, String s2, String s) {
        return isInterleave(s1, 0, s2, 0, s, 0);
    }


    public boolean isInterleave(String s1, int i1, String s2, int i2, String s, int i){
        if(i>=s.length()){
            if(i1>=s1.length() && i2>=s2.length()) return true;
            else    return false;
        }

        if(i1 < s1.length() && s1.charAt(i1)==s.charAt(i) && isInterleave(s1,i1+1,s2,i2,s,i+1))
            return true;

        if(i2 < s2.length() && s2.charAt(i2)==s.charAt(i) && isInterleave(s1,i1,s2,i2+1,s,i+1))
            return true;
        return false;
    }
}
思路2:用一个二维数组DP[i][j]来表示s1当前长度为i, s3当前长度为j, s2当前长度为j-i时是否正确interleaving. (这里也能用i, j 分别表示s1, s2的当前长度)。确定了备忘录,只有得确定递推关系。DP[i][j]的子问题有二个: DP[i][j-1]和DP[i-1][j-1],DP[i][j-1]是只有s2与s3匹配上了,DP[i-1][j-1]表示s1与s3匹配上了。

class Solution {
public:
    bool isInterleave(string s1, string s2, string s3) {
        const int n1=s1.length();
        const int n2=s2.length();
        const int n3=s3.length();
        if(n3!=n1+n2) return false;
        
        bool dp[n3+1][n2+1];
        for(int i=0; i<=n3; i++)
            for(int j=0; j<=n1; j++)
                dp[i][j]=false;
        dp[0][0]=true; 
        
        for(int i=0; i<n3; i++){  
            for(int j=0; j<=n1 && j<=i; j++){  //s1的长度在[0, n1]之间
                if(!dp[i][j]) continue;  
                int k=i-j;
                if(k<n2 && s2[k]==s3[i]) 
                    dp[i+1][j]=true;
                if(s1[j]==s3[i]) 
                    dp[i+1][j+1]=true;
            }
        }
        return dp[n3][n1];
    }
};

class Solution {
public:
    bool isInterleave(string s1, string s2, string s3) {
        const int n1=s1.length();
        const int n2=s2.length();
        const int n3=s3.length();
        if(n3!=n1+n2) return false;

        bool dp[n1+1][n2+1];
        for(int i=0; i<=n1; i++)
            for(int j=0; j<=n2; j++)
                dp[i][j]=false;
        dp[0][0]=true; 
        
        for(int i=0; i<=n1; i++){
            for(int j=0;j<=n2; j++){  //s2的长度在[0, n2]之间
                if(dp[i][j]==false) continue;
                int k=i+j;
                if(s2[j]==s3[k]) 
                    dp[i][j+1]=true;
                if(s1[i]==s3[k]) 
                    dp[i+1][j]=true;
            }
        }
        return dp[n1][n2];
    }
};

注意这里利用的后推的形式,个人认为这里后推比前推有优势,其一:s1, s2, s3与DP之间的index对应不需要额外校正,其二,由于递推只有在子问题是true上递推才成立,否则都是白谈,但是这里有两个子问题,后推只有判读一次即可。


题目二:Edit Distance

Given two words word1 and word2, find the minimum number of steps required to convert word1 to word2. 3 operations are permitted: insert, delete, replace.

思路:用二维数组dp[i][j]分别表示长度为i的s1转换为长度为j的s2时需要的最小操作数。dp[i][j]有三个子问题,dp[i-1][j], dp[i][j-1]和dp[i-1][j-1], dp[i-1][j]表示insert, dp[i][j-1]表示insert, dp[i-1][j]表示delete,而dp[i-1][j-1]表示replace。

class Solution {
public:
    int minDistance(string word1, string word2) {
        const int m=word1.length();
        const int n=word2.length();
        
        int dp[m+1][n+1];
        for(int i=0; i<=m; i++) dp[i][0]=i;
        for(int i=0; i<=n; i++) dp[0][i]=i;
        
        for(int i=1; i<=m; i++){
            for(int j=1; j<=n; j++){
                int minDis=min(dp[i-1][j]+1, dp[i][j-1]+1);
                minDis=min(minDis, dp[i-1][j-1]+(word1[i-1]==word2[j-1]?0:1));
                dp[i][j]=minDis;
            }
        }
        return dp[m][n];
    }
};

题目三:Distinct Subsequences

Given a string S and a strinig T, count the number of distinct subsequences of T in S. [subsequence is not substring!]

思路:这个题大概只能用DP来解了吧,用递归什么的根本无所下下手啊。难题!dp[i][j]表示T(1..i)中不同S(1..j)子序列。子问题有两种情况,当T[i]==S[j]时,继续使用S[j]; 当ST[i]!=S[j], 放弃S[j], S继续先前循环。

class Solution {
public:
    int numDistinct(string S, string T) {
        int n=S.length();
        int m=T.length();
        if(m>n) return 0;
        
        vector<vector<int>> num(n+1, vector<int>(m+1, 0));
        for(int k=0; k<=n; k++) 
            num[k][0] = 1;    // initialization
        
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                num[i][j]=num[i-1][j]+(S[i-1]==T[j-1]?num[i-1][j-1]:0);
            }
        }
        return num[n][m];
    }
};
注意,因为只有更新至有n-1步骤,故改二维数组可以退化为一维数组,但是时间复杂度还是O(mn). 

class Solution {
public:
    int numDistinct(string S, string T) {
        int n=S.length();
        int m=T.length();
        if(m>n) return 0;
        vector<int> num(m+1,0);
        num[0]=1;
        for(int i=1;i<=n;i++){
            for(int j=m;j>0;j--){
                num[j]=num[j]+(S[i-1]==T[j-1]?num[j-1]:0);
            }
        }
        return num[m];
    }
};






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值