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

posted @ 2013-08-16 23:50  曾见绝美的阳光  阅读(388)  评论(0编辑  收藏  举报