Interleaving String -- leetcode

来源:互联网 发布:软件下载系统网站源码 编辑:程序博客网 时间:2024/06/06 16:48

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