zoukankan      html  css  js  c++  java
  • leetcode--Interleaving String

    1.题目描述

    Given s1, s2, s3, 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.

    2.解法分析

    最开始我的思路很简单,因为此题很像最长公共子序列,所以我想先求出s1串和s3的最长公共子序列,并记录s1在s3中的位置,如果最长公共子序列就是s1,那么删除s1对应位置的字符,然后与s2比较,于是有了一下的代码:

    class Solution {
    public:
     
         bool isInterleave(string s1, string s2, string s3) {
            // Start typing your C/C++ solution below
            // DO NOT write int main() function 
            int len1=s1.length();
            int len2=s2.length();
            int len3=s3.length();
            
            if(!len1&&!len2&&!len3)return true;
            if(len1+len2!=len3)return false;
            if(!len1)return s2==s3;
            
            
            vector<vector<int>>dp;
            vector<vector<char>>path;
            
            for(int i=0;i<len1+1;++i)
            {
                vector<int> temp1;temp1.assign(len3+1,0);dp.push_back(temp1);
                vector<char>temp2;temp2.assign(len3+1,0);path.push_back(temp2);         
            }
            
            for(int i=1;i<=len1;i++)
            {
                for(int j=1;j<=len3;j++)
                {
                    if(s1[i-1]==s3[j-1])
                    {
                        dp[i][j]=dp[i-1][j-1]+1;path[i][j]='=';
                    }
                    else 
                    {
                        if(dp[i][j-1]>dp[i-1][j])
                        {
                            dp[i][j]=dp[i][j-1];path[i][j]='<';
                        }
                        else
                        {
                            if(dp[i][j-1]==dp[i-1][j])
                            {
                                dp[i][j]=dp[i][j-1];
                                path[i][j]='.';
                            }
                            else {
                                dp[i][j]=dp[i-1][j];path[i][j]='^';
                            }
                        }
                    }
                }
            }
            
            if(dp[len1][len3]!=len1)return false;
            
            if(!len2)return true;
            
            int i=len1;int j=len3;
          vector<int>loc;
            while(i&&j)
            {
                if(path[i][j]=='=')
                {
                    loc.push_back(j-1);
                    --i;--j;
                }
                else
                    if(path[i][j]=='^')--i;
                    else --j;
            }
     
                string temp=s2;
            
                vector<int>::reverse_iterator riter;
                int jj=0;int kk=len1-1;
                for(int ii =0;ii<len3;++ii)
                {
                    if(kk==-1){temp[jj]=s3[ii];jj++;continue;}
                    if(ii==loc[kk]){kk--;}
                    else {temp[jj]=s3[ii];jj++;}
                }
            
                if(temp==s2)
                {
                    return true;
                }
     
     
     
        }
    };

    写完之后就觉得肯定不会AC,原因就出在动态规划找到的一条路径只是最长的路径,不能找到所有的路径,这样有可能找出的一条路径之后,s3中剩余的元素和s2中元素内容相同,但是顺序打散,即使本来应该判定为true的结果被判定成了false。实验结果证明了我的想法:

    image

    于是,我尝试着该井LCS的算法,想让输出的路径多样化,于是有了下面的代码:

    void calPath(vector<vector<char>>&path,vector<vector<int>>&result,vector<int>cur,int i,int j)
        {
            if(!i||!j){result.push_back(cur);return;}
            if(path[i][j]=='=')
            {
                cur.push_back(j-1);
                calPath(path,result,cur,i-1,j-1);
            }
            else
            {
                if(path[i][j]=='^')
                {
                    calPath(path,result,cur,i-1,j);
                }
                else
                {
                    if(path[i][j]=='<')
                    {
                        calPath(path,result,cur,i,j-1);
                    }
                    else 
                    {
                        if(path[i][j]=='.')
                        {
                            vector<int> temp=cur;
                            calPath(path,result,cur,i,j-1);
                            cur=temp;
                            calPath(path,result,cur,i-1,j);
                        }
                    }
                }
            }
        }
     
         bool isInterleave(string s1, string s2, string s3) {
            // Start typing your C/C++ solution below
            // DO NOT write int main() function 
            int len1=s1.length();
            int len2=s2.length();
            int len3=s3.length();
            
            if(!len1&&!len2&&!len3)return true;
            if(len1+len2!=len3)return false;
            if(!len1)return s2==s3;
            
            
            vector<vector<int>>dp;
            vector<vector<char>>path;
            
            for(int i=0;i<len1+1;++i)
            {
                vector<int> temp1;temp1.assign(len3+1,0);dp.push_back(temp1);
                vector<char>temp2;temp2.assign(len3+1,0);path.push_back(temp2);         
            }
            
            for(int i=1;i<=len1;i++)
            {
                for(int j=1;j<=len3;j++)
                {
                    if(s1[i-1]==s3[j-1])
                    {
                        dp[i][j]=dp[i-1][j-1]+1;path[i][j]='=';
                    }
                    else 
                    {
                        if(dp[i][j-1]>dp[i-1][j])
                        {
                            dp[i][j]=dp[i][j-1];path[i][j]='<';
                        }
                        else
                        {
                            if(dp[i][j-1]==dp[i-1][j])
                            {
                                dp[i][j]=dp[i][j-1];
                                path[i][j]='.';
                            }
                            else {
                                dp[i][j]=dp[i-1][j];path[i][j]='^';
                            }
                        }
                    }
                }
            }
            
            if(dp[len1][len3]!=len1)return false;
            
            if(!len2)return true;
            
            int i=len1;int j=len3;
          /*  vector<int>loc;
            while(i&&j)
            {
                if(path[i][j]=='=')
                {
                    loc.push_back(j-1);
                    --i;--j;
                }
                else
                    if(path[i][j]=='^')--i;
                    else --j;
            }*/
     
            vector<vector<int>> locs;
            vector<int>cur;
     
            calPath(path,locs,cur,len1,len3);
     
            vector<vector<int>>::iterator iter;
            for(iter=locs.begin();iter!=locs.end();++iter)
            {
                vector<int>loc;
                loc=*iter;
                string temp=s2;
            
                vector<int>::reverse_iterator riter;
                int jj=0;int kk=len1-1;
                for(int ii =0;ii<len3;++ii)
                {
                    if(kk==-1){temp[jj]=s3[ii];jj++;continue;}
                    if(ii==loc[kk]){kk--;}
                    else {temp[jj]=s3[ii];jj++;}
                }
            
                if(temp==s2)
                {
                    return true;
                }
            }
     
            return false;
     
        }

    但是写的过程中我又发现不能AC,因为我仍然不能输出所有的路径。

    随着代码越来越长,我觉得这条路即便能走通也不是正常的解法,正常的解法应该是简单优美的,所以我尝试着从另外一个方向来想,灵机一动就有了这个想法:

    • 对于s1,s2,s3,从开头匹配,如果s1[0],s2[0]这两个都不与s3[0]相同,那么肯定s3不是s2和s1一个交错,返回false
    • 如果有一个相同,那么随便找一个匹配掉之后接着递归,这分三种情况:
      1.s1[0]==s2[0]==s3[0] 那么去掉s1[0]和s3[0]或者去掉s2[0]和s3[0],继续递归
      2.s1[0]==s3[0]&&s2[0]!=s3[0] ,去掉s1[0]和s3[0],继续递归
      3.s2[0]==s3[0]&&s1[0]!=s3[0] ,去掉s2[0]和s3[0],继续递归

    对应代码如下,小数据集一次AC,但是大数据集超时了,想想也对,这个解法有大量的递归,理论上是比较耗时的。

    class Solution {
    public:
        bool isInterleave(string s1, string s2, string s3) {
            // Start typing your C/C++ solution below
            // DO NOT write int main() function
            
            return myIsInterleave(s1,0,s1.length()-1,s2,0,s2.length()-1,s3,0,s3.length()-1);
        }
        
        bool myIsInterleave(string &s1,int start1,int end1,string &s2,int start2,int end2,string &s3,int start3,int end3)
        {
            int len1=end1-start1+1;
            int len2=end2-start2+1;
            int len3=end3-start3+1;
            
            if(len1+len2!=len3)return false;
            if(!len1)return myIsEqual(s2,start2,end2,s3,start3,end3);
            if(!len2)return myIsEqual(s1,start1,end1,s3,start3,end3);
            
            if(s1[start1]!=s3[start3]&&s2[start2]!=s3[start3])return false;
            
            if(s1[start1]==s3[start3]&&s2[start2]==s3[start3])
            {
                return myIsInterleave(s1,start1+1,end1,s2,start2,end2,s3,start3+1,end3)||myIsInterleave(s1,start1,end1,s2,start2+1,end2,s3,start3+1,end3);
            }
            
            if(s1[start1]==s3[start3]&&s2[start2]!=s3[start3])
            {
                return myIsInterleave(s1,start1+1,end1,s2,start2,end2,s3,start3+1,end3);
            }
            
            if(s1[start1]!=s3[start3]&&s2[start2]==s3[start3])
            {
                return myIsInterleave(s1,start1,end1,s2,start2+1,end2,s3,start3+1,end3);
            }
        }
        
        bool myIsEqual(string &s1,int start1,int end1,string &s2,int start2,int end2)
        {
            int i = start1;
            int j=start2;
            while(i<=end1)
            {
                if(s1[i]!=s2[j])return false;
                ++i;++j;
            }
            
            return true;
        }
    };

    这样不行,可以换个思路,那就以空间换时间,刚才递归的算法中我隐隐约约感受到了动态规划的味道,如何确认最优子结构呢,因为有s1,s2,s3,印象中应该建立一张二维表match,match[i][j]=1表示s3[0…i+j-1]是s1[0..i]和s2[0…j]的一个交错。有了这个思路下面的代码就水到渠成了。

    class Solution {
    public:
        bool isInterleave(string s1, string s2, string s3) {
            // Start typing your C/C++ solution below
            // DO NOT write int main() function 
            
            int len1=s1.length();
            int len2=s2.length();
            int len3=s3.length();
            
            if(len1+len2!=len3)return false;
            if(!len1&&!len2)return true;
            if(!len1)return s2==s3;
            if(!len2)return s1==s3;
            
            vector<vector<int>>match;
            for(int i =0;i<=len1;++i)
            {
                vector<int> cur;
                cur.assign(len2+1,0);
                match.push_back(cur);
            }
            
            for(int i=1;i<len1+1;++i)
            {
                if(s1[i-1]==s3[i-1])match[i][0]=1;
                else break;
            }
            
            for(int i=1;i<len2+1;++i)
            {
                if(s2[i-1]==s3[i-1])match[0][i]=1;
                else break;
            }
            
            match[0][0]=1;
            for(int i =1;i<len1+1;++i)
            {
                for(int j=1;j<len2+1;++j)
                {
                    if(match[i-1][j]==1&&s1[i-1]==s3[i+j-1])match[i][j]=1;
                    if(match[i][j-1]==1&&s2[j-1]==s3[i+j-1])match[i][j]=1;
                }
            }
            
            return match[len1][len2]==1;
            
        }
    };

    结果如下,速度很快:

    image

  • 相关阅读:
    生成函数
    FFT【快速傅里叶变换】FWT【快速沃尔什变换】
    牛客网多校赛第9场 E-Music Game【概率期望】【逆元】
    hdu6393Traffic Network in Numazu【树状数组】【LCA】
    hdu 6395Sequence【矩阵快速幂】【分块】
    hdu6390GuGuFishtion【数论】
    欧拉函数和莫比乌斯函数
    hdu4614 Vases and Flowers【线段树】【二分】
    hdu4578 Transformation【线段树】
    hdu3974 Assign the task【线段树】
  • 原文地址:https://www.cnblogs.com/obama/p/3263712.html
Copyright © 2011-2022 走看看