题目:
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.
解题:
看到这道题的第一反应是动态规划,可以用递归。实现如下:
class Solution { public: bool isInterleave(string s1, string s2, string s3) { if(s1.length() + s2.length() != s3.length()) return false; return match(s1, s2, s3, 0, 0, 0); } bool match(string s1, string s2, string s3, int s1_offset, int s2_offset, int s3_offset){ if( s3_offset == s3.length())return true; bool b_s1 = false; bool b_s2 = false; if(s1_offset < s1.length() && s1[s1_offset] == s3[s3_offset]) b_s1 = match(s1, s2, s3, s1_offset+1, s2_offset, s3_offset+1); if(s2_offset < s2.length() && s2[s2_offset] == s3[s3_offset]) b_s2 = match(s1, s2, s3, s1_offset, s2_offset+1, s3_offset+1); return b_s1 || b_s2; } }
但是跑下面这个case时就超时了。自己试了一下,居然跑了22秒!
Last executed input: | "abbbbbbcabbacaacccababaabcccabcacbcaabbbacccaaaaaababbbacbb", "ccaacabbacaccacababbbbabbcacccacccccaabaababacbbacabbbbabc", "cacbabbacbbbabcbaacbbaccacaacaacccabababbbababcccbabcabbaccabcccacccaabbcbcaccccaaaaabaaaaababbbbacbbabacbbacabbbbabc" |
所以,需要改进。
对于s1,s2,s3:
s1:s1[1],s1[2],...,s1[i-1],s1[i];
s2:s2[1],s2[2],...,s2[j-1],s2[j];
s3:s3[1],s3[2],...s3[i+j-1],s3[i+j],
N[i][j]表示s1[0--i]和s2[0--j]是否匹配s3[0--i+j],如果匹配N[i][j]=1,否则N[i][j]=0。由这个定义,对于s1="aabcc",s2="dbbca",s3="aadbbcbcac",可以得到如下的二维数组。
从这个图可以看出递推关系。
首先初始条件:
N[0][0]=1;
对于i=0,j>=1的情况,N[0][j]=N[0][j-1] &&(s2[j-1]==s3[j-1]);
对于j=0,i>=1的情况,N[i][0]=N[i-1][0]&&(s1[i]==s3[i]);
在此基础上,递推关系:
N[i][j]= (s3[i+j-1] == s1[i-1]) && N[i-1][j] || (s3[i+j-1] == s2[j-1]) && N[i][j-1]).
代码实现如下:
bool isInterleave(string s1, string s2, string s3) { int ls1 = s1.size(); int ls2 = s2.size(); int ls3 = s3.size(); if(ls1 + ls2 != ls3)return false; static bool N[2000][2000]; int i,j; N[0][0] = true; for(j = 1; j <= ls2; j++) N[0][j] = N[0][j-1] && (s2[j-1] == s3[j-1]); for(i = 1; i <= ls1; i++) N[i][0] = N[i-1][0] && (s1[i-1] == s3[i-1]); for(i = 1; i <= ls1; i++) for(j = 1; j <= ls2; j++) N[i][j]= ((s3[i+j-1] == s1[i-1]) && N[i-1][j]) || ((s3[i+j-1] == s2[j-1]) && N[i][j-1]); return N[ls1][ls2]; }
通过,6ms,表现还不错。把数组的声明改成static bool N[200][200],可以降到4ms。
还可以继续改进,用两个一维数组交替来表示行,因为上面二维数组里的每一个元素只与他的上一个和左边一个有关系。
bool isInterleave(string s1, string s2, string s3) { int ls1 = s1.size(); int ls2 = s2.size(); int ls3 = s3.size(); if(ls1 + ls2 != ls3)return false; bool *n1 = new bool[ls2+1]; bool *n2 = new bool[ls2+1]; int i,j; n1[0] = true; n2[0] = true; for(j = 1; j <= ls2; j++) n1[j] = n1[j-1] && (s2[j-1] == s3[j-1]); for(i = 1; i <= ls1; i++){ n2[0] = n1[0] && (s1[i-1] == s3[i-1]); for(j = 1; j <= ls2; j++) n2[j]= (s3[i+j-1] == s1[i-1]) && n1[j] || (s3[i+j-1] == s2[j-1]) && n2[j-1]; bool* tmp = n1; n1 = n2; n2 = tmp; } bool r = n1[ls2]; delete n1; delete n2; return r; }
依然是6ms,但是不用构造一个庞大的二维数组,在空间上节省了很多。 还可以优化,先比较ls1和ls2的大小,选其中小的+1作为n1和n2的长度。
参考:http://fisherlei.blogspot.hk/2012/12/leetcode-interleaving-string.html