分析
题目给定3个字符串(s1,s2,s3),看s3是否是有s1和s2通过交织可以得到。
可以这么来看这个问题,每次从s3开头拿出来一个字符,如果s1的开头或者s2的开头有这个字符的话,就消掉相应的字符,把这个操作记为del。
这样看待这个问题的话,好像挺简单的,很直观。
但是当遇到case:s1=aa,s2=ab,s3=abaa,的时候可以看出当s1和s2同时可以进行del操作的时候,选择就成了一个必须考虑的问题。
假如最开始那个a,消掉了s1中的第一个a,那么就进行不下去了。
所以最后这个问题其实并不那么简单,假如函数
bool isInterleaving(string &s1, int len1, string &s2, int len2, string &s3, int len3);
表示子问题:si取前leni个字符的话,那么实际上可以得到这样的一个公式:
isInterleaving(s1,len1,s2,len2,s3,len3) = (s3.lastChar == s1.lastChar) && isInterleaving(s1,len1 - 1,s2,len2,s3,len3 - 1) ||(s3.lastChar == s2.lastChar) && isInterleaving(s1,len1,s2,len2 - 1,s3,len3 - 1)
由于len3 === len1 + len2,所以这个问题里面实际上存在着两个变量,是一个二维动态规划题目。
从矩阵的角度来看的话,每一个元素的值,依赖于它的上边和左边两个值。
最后写出程序,用24ms过大测试集合。
题目描述:Interleaving String
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(s3.size() != s1.size() + s2.size()) return false; //create indicator vector<vector<bool> > match(s1.size() + 1, vector<bool>(s2.size() + 1, false)); //initialization the first row and the first column match[0][0] = true; for( int l1 = 1; l1 <= s1.size(); ++ l1 ) { char c1 = s1[l1 - 1]; char c3 = s3[l1 - 1]; if (c1 == c3) { match[l1][0] = true; } else break; } for( int l2 = 1; l2 <= s2.size(); ++ l2 ) { char c2 = s2[l2 - 1]; char c3 = s3[l2 - 1]; if (c2 == c3) { match[0][l2] = true; } else break; } //work through the rest of matrix using the formula for( int l1 = 1; l1 <= s1.size(); ++ l1 ) { char c1 = s1[l1 - 1]; for( int l2 = 1 ; l2 <= s2.size() ; ++ l2 ) { char c2 = s2[l2 - 1]; int l3 = l1 + l2; char c3 = s3[l3 - 1]; if (c1 == c3) { match[l1][l2] = match[l1 - 1][l2] || match[l1][l2]; } if (c2 == c3) { match[l1][l2] = match[l1][l2 - 1] || match[l1][l2]; } } } //the last element is the result return match[s1.size()][s2.size()]; } };
几个失败的想法
一开始的想法,错在当s1,s2同时匹配的时候,这里存在一个策略问题,比如测试用例:aa,ab,abaa,就过不了。
class Solution { public: bool isInterleave(string s1, string s2, string s3) { if(s3.size() != s1.size() + s2.size()) return false; int i1 = 0; int i2 = 0; for(int i3 = 0 ; i3 < s3.size(); ++i3) { if(i1 < s1.size() && s1[i1] == s3[i3]) { ++i1; } else if (i2 < s2.size() && s2[i2] == s3[i3]) { ++i2; } else return false; } return true; } };
第二个错误想法,先从s3中标记掉s1中出现的内容,然后在看剩下的部分是不是s2,也不对,一样的存在“标记掉s1″的策略问题
class Solution { public: bool isInterleave(string s1, string s2, string s3) { if(s3.size() != s1.size() + s2.size()) return false; vector<bool> visited(s3.size(),false); int i3 = 0; for( int i1 = 0; i1 < s1.size() ; ++i1 ) { bool match = false; while(i3 < s3.size()) { if(s3[i3] == s1[i1]) { visited[i3 ++] = true; match = true; break; } ++i3; } if(!match) return false; } i3 = 0; for( int i2 = 0; i2 < s2.size() ; ++i2 ) { bool match = false; while(i3 < s3.size()) { if (visited[i3]) { ++i3; continue; } if(s3[i3 ++] == s2[i2]) { match = true; break; } } if(!match) return false; } return true; } };
所以,绕不开策略问题,就得在做出决策的时候让程序分支。下面是一个正确但大集合会超时的递归算法:
class Solution { public: bool isInterleave(string &s1,int i1, string &s2, int i2, string &s3, int i3) { if(i1 == s1.size() && i2 == s2.size() && i3 == s3.size()) return true; bool match = false; if(i1 < s1.size() && s1[i1] == s3[i3]) { match = isInterleave(s1, i1 + 1, s2, i2, s3, i3 + 1); } if (!match && i2 < s2.size() && s2[i2] == s3[i3]) { match = isInterleave(s1, i1, s2, i2 + 1, s3, i3 + 1); } return match; } bool isInterleave(string s1, string s2, string s3) { if(s3.size() != s1.size() + s2.size()) return false; return isInterleave(s1,0,s2,0,s3,0); } };