zoukankan      html  css  js  c++  java
  • 【Interleaving String】cpp

    题目:

    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.

    代码:

    “merge sort + stack”

            struct LastMatch{
                int i1;
                int i2;
                int i3;
                LastMatch(int i1, int i2, int i3): i1(i1), i2(i2), i3(i3){}
            };
            static bool isInterleave(string s1, string s2, string s3)
            {
                const int n1 = s1.size();
                const int n2 = s2.size();
                const int n3 = s3.size();
                if ( n1+n2 != n3 ) return false;
                stack<LastMatch> sta;
                int i1=0,i2=0,i3=0;
                while ( i1<n1 || i2<n2 || i3<n3 )
                {
                    if ( s1[i1]==s2[i2] && s2[i2]==s3[i3] ){
                        sta.push(LastMatch(i1,i2,i3));
                        i1++; i3++;
                    }
                    else if ( s1[i1]==s3[i3] ){ 
                        i1++; i3++;
                    }
                    else if ( s2[i2]==s3[i3] ){ 
                        i2++; i3++; 
                    }
                    else if ( !sta.empty() ){
                        LastMatch lm = sta.top(); sta.pop();
                        i1 = lm.i1; i2 = lm.i2; i3 = lm.i3;
                        i2++; i3++;
                    }
                    else{
                        return false;
                    }
                }
                return i1==n1 && i2==n2 && i3==n3;
            }

    "dp"

    class Solution {
    public:
            bool isInterleave(string s1, string s2, string s3)
            {
                const int n1 = s1.size();
                const int n2 = s2.size();
                const int n3 = s3.size();
                if ( n1+n2 != n3 ) return false;
                vector<vector<bool> > dp(n1+1, vector<bool>(n2+1, false));
                dp[0][0] = true;
                // check s1 boundary
                for ( int i = 1; i <= n1; ++i ){
                    dp[i][0] = s1[i-1]==s3[i-1] && dp[i-1][0];
                }
                // check s2 boundary
                for ( int i = 1; i <= n2; ++i ){
                    dp[0][i] = s2[i-1]==s3[i-1] && dp[0][i-1];
                }
                // dp process
                for ( int i = 1; i<=n1; ++i )
                {
                    for ( int j = 1; j<=n2; ++j )
                    {
                        dp[i][j] = ( s1[i-1]==s3[i+j-1] && dp[i-1][j] )
                        || ( s2[j-1]==s3[i+j-1] && dp[i][j-1] );
                    }
                }
                return dp[n1][n2];
            }
    };

    tips:

    这道题第一版采用“merge sort + stack”,有一个大集合过不去,报超时(但即使跟可以AC的dp方法对比,“merge sort+stack”过这个大集合也仅仅慢了不到10%,在数量级上应该没有差别,时间复杂度都是O(n²))

    dp的解法是学习网上的解法,理解如下:

    dp[i][j]表示s1[0~i-1]与s2[0~j-1]是否匹配s3[0~i+j-1]

    因此为了方便,定义dp[n+1][m+1],多一个维度,目的是保证从s1中取的个数从0到n都可以表示(s2同理)。

    可以写出来dp的通项公式:

    dp[i][j] = ( s1[i-1]==s3[i+j-1] && dp[i-1][j] ) || ( s2[j-1]==s3[i+j-1] && dp[i][j-1] )

    表示s3第i+j个元素要么由s1匹配上,要么由s2匹配上。

    最后返回dp[n1][n2]就是所需的结果。

    整个dp的过程并不复杂,思考下如何得来的:

    1. dp取两个维度是因为s1和s2两个变量

    2. 之前自己思考dp的时候,考虑的是每个位置可以由s1或者s2其中的元素表示,但是这样考虑起来就太复杂了;网上的思路并么有考虑到这么复杂,而是仅仅考虑s3中总共就这么长字符串,某个长度的字符串可以从s1和s2各取几个

    ============================================

    上述的dp过程的空间复杂度是O(n²)的,再采用滚动数组方式,把空间复杂度降低到O(n),代码如下:

    class Solution {
    public:
            bool isInterleave(string s1, string s2, string s3)
            {
                const int n1 = s1.size();
                const int n2 = s2.size();
                const int n3 = s3.size();
                if ( n1+n2 != n3 ) return false;
                vector<bool> dp(n2+1, false);
                // check s2 boundary
                dp[0] = true;
                for ( int i = 1; i<=n2; ++i )
                {
                    dp[i] = s2[i-1]==s3[i-1] && dp[i-1];
                }
                // dp process
                for ( int i = 1; i<=n1; ++i )
                {
                    dp[0] = s1[i-1]==s3[i-1] && dp[0];
                    for ( int j = 1; j<=n2; ++j )
                    {
                        dp[j] = ( s1[i-1]==s3[i+j-1] && dp[j] ) || ( s2[j-1]==s3[i+j-1] && dp[j-1] ); 
                    }
                }
                return dp[n2];
            }
    };

    tips:如果二维的dp过程只跟紧上一次的dp过程有关,就可以退化为滚动数组形式的一维dp过程。

    =======================================

    第二次过这道题,参照dp的思路写了一遍。有个细节就是dp递推公式的时候“s3[i+j-1]”不要写成s3[i-1]或者s3[j-1]。

    class Solution {
    public:
        bool isInterleave(string s1, string s2, string s3) {
                if ( (s1.size()+s2.size())!=s3.size() ) return false;
                bool dp[s1.size()+1][s2.size()+1];
                fill_n(&dp[0][0], (s1.size()+1)*(s2.size()+1), false);
                dp[0][0] = true;
                for ( int i=1; i<=s1.size(); ++i )
                {
                    dp[i][0] = dp[i-1][0] && s1[i-1]==s3[i-1];
                }
                for ( int i=1; i<=s2.size(); ++i )
                {
                    dp[0][i] = dp[0][i-1] && s2[i-1]==s3[i-1];
                }
                for ( int i=1; i<=s1.size(); ++i )
                {
                    for ( int j=1; j<=s2.size(); ++j )
                    {
                        dp[i][j] = ( dp[i-1][j] && s1[i-1]==s3[i+j-1] ) || 
                                   ( dp[i][j-1] && s2[j-1]==s3[i+j-1] );
                    }
                }
                return dp[s1.size()][s2.size()];
        }
    };
  • 相关阅读:
    [Kali_Debian] 清除无用的库文件(清理系统,洁癖专用)-布布扣-bubuko.com
    给 Linux 系统“减肥”,系统垃圾清理_系统安装与配置管理_Linux Today
    命令行选项
    SQL 优化
    精通initramfs构建step by step
    常用正则表达式
    Chrome_浏览器开发人员工具
    按键精灵
    CMD命令大全
    50种折纸方法
  • 原文地址:https://www.cnblogs.com/xbf9xbf/p/4547461.html
Copyright © 2011-2022 走看看