Interleaving String -- leetcode

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[i]和s2[j]之前的字符,和s3[i+j+1]之前的字符,已经满足interleave条件,则添加新字符是否能组成新的interleave的条件为:

若 s1[i] == s3[i+j+1]  或者 s2[j] == s3[i+j+1]


class Solution {
public:
    bool isInterleave(string s1, string s2, string s3) {
        return helper(s1.data(), s2.data(), s3.data());
    }
    
    bool helper(const char *s1, const char *s2, const char *s3) {
        if (!*s3) return !*s1 && !*s2;
        
        return *s1 == *s3 && helper(s1+1, s2, s3+1) ||
               *s2 == *s3 && helper(s1, s2+1, s3+1);
    }
};

但是简单递归,时间是却无法被AC。如下面的这样的case:

s1 = "bbbbbabbbbabaababaaaabbababbaaabbabbaaabaaaaababbbababbbbbabbbbababbabaabababbbaabababababbbaaababaa",

s2 = "babaaaabbababbbabbbbaabaabbaabbbbaabaaabaababaaaabaaabbaaabaaaabaabaabbbbbbbbbbbabaaabbababbabbabaab",

s3  ="babbbabbbaaabbababbbbababaabbabaabaaabbbbabbbaaabbbaaaaabbbbaabbaaabababbaaaaaabababbababaababbababbbababbbbaaaabaabbabbaaaaabbabbaaaabbbaabaaabaababaababbaaabbbbbabbbbaabbabaabbbbabaaabbababbabbabbab"


稍微改进了一下,加入访问标志。这下运行时间也能被AC了。下面代码在leetcode上实际执行时间为5ms。

visited访问标志,表示,该对位置(s1,s2),是否已经被判断过,如果已经判断过。则说明从该位置继续下去没有意义。因为如果行,则早就成功了。

注,虽然地址是64bit的,但由于字符串是连续的。所以用低32bit,足已代表该地址。

class Solution {
public:
    bool isInterleave(string s1, string s2, string s3) {
        unordered_set<uint64_t> visited;
        return helper(s1.data(), s2.data(), s3.data(), visited);
    }
    
    bool helper(const char *s1, const char *s2, const char *s3, unordered_set<uint64_t> &visited) {
        if (!*s3) return !*s1 && !*s2;
        const uint64_t xy = (uint64_t)s1 << 32 | ((uint64_t)s2 & 0xffffffff);
        if (visited.find(xy) != visited.end()) return false;
        visited.insert(xy);
        return *s1 == *s3 && helper(s1+1, s2, s3+1, visited) ||
               *s2 == *s3 && helper(s1, s2+1, s3+1, visited);
    }
};



算法二:

dp[i][j]表示, s1的[0,i]子串,s2的[0,j]的子串,是否能满足s3的[0, i+j+1]子串的interleave关系。

要求出dp[i][j],需要 dp[i-1][j] 和 dp[i][j-1]两个历史信息。

即: dp[i][j] = dp[i-1][j] && s1[i] == s3[i+j+1] || dp[i][j-1] && s2[j] == s3[i+j+1];

需要一个二维的数组做辅助空间。

但是,由于所需的历史信息,其实是当前的列的前一行,以及当前的行的前一列。 即正上和正左。

这样的情况,可以用滚动数组,去代替二维数组。

即,只需要保留上一行的信息,以及前一列的信息。

故只需要一维数组就行了。 为进一步进省内存,可以选择较短字符的长度作为辅助空间。

class Solution {
public:
    bool isInterleave(string s1, string s2, string s3) {
        if (s1.size() + s2.size() != s3.size())
            return false;
        if (s2.size() > s1.size())
            s2.swap(s1);
        
        vector<char> dp(s2.size()+1);
        dp[0] = 1;
        for (int j=0; j<s2.size(); j++)
            dp[j+1] = dp[j] && s2[j] == s3[j];
            
        for (int i=0; i<s1.size(); i++) {
            dp[0] = dp[0] && s1[i] == s3[i];
            for (int j=0; j<s2.size(); j++) {
                dp[j+1] = dp[j] && s2[j] == s3[i+j+1] || dp[j+1] && s1[i] == s3[i+j+1];
            }
        }
        return dp[s2.size()];
    }
};


算法三:宽度优先搜索

假如 s1 = "aab" and s2 = "abc". s3 = "aaabcb". s1和s2可以组成一个棋盘,或者晶格。如下:

问题就可以转变成,从左上角,到右下角,寻找一条路径,使得路径所经过的字符为s3。

路径上,每一步,只能向右或者向下。

而广度优先搜索,是非常适合处理该问题的。

o--a--o--b--o--c--o
|     |     |     |
a     a     a     a
|     |     |     |
o--a--o--b--o--c--o
|     |     |     |
a     a     a     a
|     |     |     |
o--a--o--b--o--c--o
|     |     |     |
b     b     b     b
|     |     |     |
o--a--o--b--o--c--o
该代码在leetcode上实际执行时间为5ms。

class Solution {
public:
    bool isInterleave(string s1, string s2, string s3) {
        if (s1.size() + s2.size() != s3.size()) return false;
        unordered_set<uint64_t> visited;
        queue<uint64_t> q;
        q.push(0);
        q.push(-1);
        int level = 0;
        while (!(q.size() == 1 && q.front() == -1)) {
            const auto xy = q.front();
            q.pop();
            const uint64_t x = xy & 0xffffffff;
            const uint64_t y = xy >> 32;
            if (x == s1.size() && y == s2.size())
                return true;
            else if (xy == -1) {
                q.push(xy);
                ++level;
                continue;
            }
            
            if (visited.find(xy) != visited.end())
                continue;
                
            visited.insert(xy);
            
            if (x < s1.size() && s1[x] == s3[level])
                q.push(y << 32 | x+1);
                
            if (y < s2.size() && s2[y] == s3[level])
                q.push((y+1) << 32 | x);
        }
        
        return false;
    }
};

该算法参考自:

https://leetcode.com/discuss/19973/8ms-c-solution-using-bfs-with-explanation


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值