LeetCode OJ - 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.

分析:简单暴力流DFS,效率不过。需要考虑其他方法

方法一:DFS居然没有超时, 你敢信。而且如果我不加个dp表,它还会进入“死循环”,这一点开始不能理解。

class Solution {
    bool ret;
    int dp[1000][1000];
public:
    bool isInterleave(string s1, string s2, string s3) {
        if(s1.size() + s2.size() != s3.size()) return false;
        DFS(s1, s2, s3, 0, 0, 0);
        return ret;
    }
    
    void DFS(string &s1, string&s2, string &s3, int i, int j, int k) {
        if(ret || dp[i][j]) return;
        dp[i][j] = true; 
        
        if(k == s3.size()) {
            ret = true;
            return ;
        }
        
        if(i < s1.size() && s1[i] == s3[k]) DFS(s1, s2, s3, i + 1, j, k + 1);
        if(j < s2.size() && s2[j] == s3[k]) DFS(s1, s2, s3, i, j + 1, k + 1);        
    }
};

死循环的原因在于下面这张dp[i][j]表:


这里每次都更新了dp[i][j], dp[i][j] 表示s1[i] 和 s2[j] 准备和 s3[i + j]比较的情况次数。在DFS过程中,若dp[i][j] 表示一种状态,那么到另外一种状态可能有很多方式,再到下一种状态又有很多方式,类似一颗树的结构。而状态数量的增长应该和指数增长速度相当(定性分析),这样某种状态会重复出现,出现“死循环”的假象。

简单数据证明:

string s1 = "bbbbbabbbbabaababaaaabbababbaaabbabbaaabaaaaababbbababbbbbabbbbababbabaabababbbaabababababbbaaababaa";
string s2 = "babaaaabbababbbabbbbaabaabbaabbbbaabaaabaababaaaabaaabbaaabaaaabaabaabbbbbbbbbbbabaaabbababbabbabaab";
string s3 = "babbbabbbaaabbababbbbababaabbabaabaaabbbbabbbaaabbbaaaaabbbbaabbaaabababbaaaaaabababbababaababbababbbababbbbaaaabaabbabbaaaaabbabbaaaabbbaabaaabaababaababbaaabbbbbabbbbaabbabaabbbbabaaabbababbabbabbab";
         上图中最右下角的那个数字2,为dp[45][32] = 2,表示函数递归进入i = 45、j = 32两次。若不加dp表,让函数递归每次打印 i, j, k, 会看到 i = 45、j = 32、k = 77这个数据一直重复出现。这也就是上面解释的“死循环”的假象, 原因是某些状态重复出现,状态转移导致规模指数增长。 


方法二:迭代dp,感觉跟二位地图测试是否可以从左上角可达右下角问题类似,不过要注意边界。

              设dp[i][j] 表示取 i 个s1元素和 j 个s2 元素,是否能与 s3匹配。这样就可以建立dp[i][j]、dp[i][j-1]、dp[i-1][j] 之间的关系了

class Solution {
    int dp[1000][1000];
public:
    bool isInterleave(string s1, string s2, string s3) {
        int len1 = s1.size();
        int len2 = s2.size();
        int len3 = s3.size();
        
        if(len1 + len2 != len3) return false;
        
        dp[0][0] = true;
        for(int i = 0; i < len1; i++) {
            if(s1[i] == s3[i]) dp[i + 1][0] = true;
            else break;
        }
        for(int j = 0; j < len2; j++) {
            if(s2[j] == s3[j]) dp[0][j + 1] = true;
            else break;
        }
        
        for(int i = 1; i <= len1; i++) {
            for(int j = 1; j <= len2; j++) {
                if(s1[i - 1] == s3[i+j - 1] && dp[i-1][j]) {
                    dp[i][j] = true;
                } else if(s2[j - 1] == s3[i+j - 1] && dp[i][j-1]) {
                    dp[i][j] = true;
                }
            }
        }
        
        return dp[len1][len2];
    }
};

时间复杂度与方法一是一样的 O(len1 * len2) , 题目跟  LeetCode OJ - Minimum Path Sum 异曲同工



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值