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。实验结果证明了我的想法:
于是,我尝试着该井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;
}};
结果如下,速度很快: