zoukankan      html  css  js  c++  java
  • [LeetCode]Interleaving String关于遍历和动态规划

    晚上做了一下leetcode的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.

    第一反应很适合用递归:

        bool isInterleave_res(string s1, string s2, string s3) {
            if ((s1.empty() && s2.empty() && s3.empty()) || 
                (!s1.empty() && !s3.empty() && s3[0] == s1[0] && isInterleave_res(s1.substr(1, s1.length()-1), s2, s3.substr(1, s3.length()-1))) || 
                (!s2.empty() && !s3.empty() && s3[0] == s2[0] && isInterleave_res(s1, s2.substr(1, s2.length()-1), s3.substr(1, s3.length()-1))) )
            {
                return true;
            }
            return false;
        }

    这样本质上就是树的深度优先遍历,但是效率就比较低,很多节点会多次计算到,果然TLE,用例是:

    s1 = "bbbbbabbbbabaababaaaabbababbaaabbabbaaabaaaaababbbababbbbbabbbbababbabaabababbbaabababababbbaaababaa";
    s2 = "babaaaabbababbbabbbbaabaabbaabbbbaabaaabaababaaaabaaabbaaabaaaabaabaabbbbbbbbbbbabaaabbababbabbabaab";
    s3 = "babbbabbbaaabbababbbbababaabbabaabaaabbbbabbbaaabbbaaaaabbbbaabbaaabababbaaaaaabababbababaababbababbbababbbbaaaabaabbabbaaaaabbabbaaaabbbaabaaabaababaababbaaabbbbbabbbbaabbabaabbbbabaaabbababbabbabbab";

    这题很容易也想到用动态规划,设置表dp,dp[i][j]代表s1的前i个和s2的前j个能和s3的前i+j个匹配上,则根据dp[i][j]就可以推出dp[i+1][j]和dp[i][j+1]。返回dp最右下角的值。

    例如题目中的例子1:s3 = "aadbbcbcac"

    例子2:s3 = "aadbbcbccc"

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

    这样代码就通过了。

    仔细观察,这题用遍历就是一棵二叉树的深度优先遍历,而这棵树对应dp的表格就是(i, j)的左节点就是(i+1, j)右节点是(i, j+1),所以(i+1, j)的右节点和(i, j+1)的左节点是同样的(i+1, j+1),进一步加剧了遍历树的重复。

    但是观察dp表,其实也浪费了很多,如果s1长度为m而s2长度为n,DP的时间复杂度和空间复杂度都为m*n(空间复杂度可以优化为2×m或者2×n,因为只和上一行相关),但是其实我们关心的只是其中为T的点,最好情况下其实只需要m+n。

    我们可以将树的遍历(只走了为T的点)和DP表结合起来,可以理解为将DP表用稀释矩阵的形式保存,只保存T的点,而走过的T点就不再遍历,这样在m和n很大的情况下应该有较大提升,代码如下:

    class Solution {
    public:
        bool isInterleave(string s1, string s2, string s3) {
            int len1 = s1.length(), len2 = s2.length(), len3=s3.length();
            if (len1+len2!=len3)
            {
                return false;
            }
            stack<pair<int, int>> node;
            set<pair<int, int>> passed;
            node.push(pair<int, int>(-1, -1));
            pair<int, int> last_node;
            int count = 0;
            while (!node.empty())
            {
                count++;
                int i = node.top().first;
                int j = node.top().second;
                passed.insert(pair<int, int>(i, j));
                if (i == len1-1 && j == len2-1)
                {
                    return true;
                }
                node.pop();
                if (i+1 < len1 && i+j+2 < len3 && s1[i+1]==s3[i+j+2] && passed.find(pair<int, int>(i+1, j)) == passed.end())
                {
                    node.push(pair<int, int>(i+1, j));
                }
                if (j+1 < len2 && i+j+2 < len3 && s2[j+1]==s3[i+j+2] && passed.find(pair<int, int>(i, j+1)) == passed.end())
                {
                    node.push(pair<int, int>(i, j+1));
                }
            }
            return false;
        }
    };

    三种方法,第一种,Time Limit Exceeded;第二种16ms通过所有测试用例,第三种8ms

  • 相关阅读:
    Hibernate save, saveOrUpdate, persist, merge, update 区别
    Eclipse下maven使用嵌入式(Embedded)Neo4j创建Hello World项目
    Neo4j批量插入(Batch Insertion)
    嵌入式(Embedded)Neo4j数据库访问方法
    Neo4j 查询已经创建的索引与约束
    Neo4j 两种索引Legacy Index与Schema Index区别
    spring data jpa hibernate jpa 三者之间的关系
    maven web project打包为war包,目录结构的变化
    创建一个maven web project
    Linux下部署solrCloud
  • 原文地址:https://www.cnblogs.com/yezhangxiang/p/3952659.html
Copyright © 2011-2022 走看看