LeetCode 97. 交错字符串

难度:中等。
标签:字符串,动态规划。

回溯

开始使用回溯法直接算,第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);
    }
};

慢慢优化:

  1. 将dfs中的k3去掉,k3可以表示为k1+k2,至此dfs变为k1,k2和choose的函数。
  2. 使用两个二维矩阵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];
    }
};

结果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值