Given a string s1, we may represent it as a binary tree by partitioning it to two non-empty substrings recursively.
Below is one possible representation of s1 = "great"
:
great / \ gr eat / \ / \ g r e at / \ a t
To scramble the string, we may choose any non-leaf node and swap its two children.
For example, if we choose the node "gr"
and swap its two children, it produces a scrambled string "rgeat"
.
rgeat / \ rg eat / \ / \ r g e at / \ a t
We say that "rgeat"
is a scrambled string of "great"
.
Similarly, if we continue to swap the children of nodes "eat"
and "at"
, it produces a scrambled string "rgtae"
.
rgtae / \ rg tae / \ / \ r g ta e / \ t a
We say that "rgtae"
is a scrambled string of "great"
.
Given two strings s1 and s2 of the same length, determine if s2 is a scrambled string of s1.
题目解析:
凡是树的问题,就自然想到了递归算法。不过自己想的算法跟“判断前序遍历和后序遍历是否为同一个树”的方法一样。但导致了问题:
错误代码:
class Solution {
public:
bool isScramble(string s1, string s2) {
if(s1.size() != s2.size())
return false;
return JudgeScramble(s1,0,s1.size()-1,s2,0,s2.size());
}
bool JudgeScramble(string s1,int begin1,int end1,string s2,int begin2,int end2){
if(begin1 > end1)
return true;
int i = begin2;
while(i <= end2){
if(s1[begin1] == s2[i])
break;
i++;
}
if(i > end2)
return false;
int len = i-begin2;
return JudgeScramble(s1,begin1+1,begin1+len,s2,begin2,i-1) && JudgeScramble(s1,begin1+len+1,end1,s2,i+1,end2);
}
};
出错案例
然后就没有想到更合适的方法,一直在树的范围内挣扎。上网看了别人的方案后才知道,是按照字符串的形式来处理的。而对于分割点则采用枚举的方法……就是把s1分为两个串a1,a2;s2分为两个串b1,b2,只要长度相同而且能对应上(就是可以通过交换得来)就可以了,然后递归判断每个子串。下面是递归的方案:
bool isScramble(string s1, string s2) {
int lens1,lens2;
lens1 = s1.length();
lens2 = s2.length();
if(lens1!=lens2)
return false;
if(lens1==0)
return true;
if (lens1==1)
{
return s1[0]==s2[0];
}
if (lens2==2)
{
return (s1[0]==s2[0]&&s1[1]==s2[1])||(s1[0]==s2[1]&&s1[1]==s2[0]);
}
string stra1,stra2,strb1,strb2;
for(int i=1;i<lens1;i++)
{
stra1 = s1.substr(0,i);
stra2 = s1.substr(i,lens1-i);
strb1 = s2.substr(0,i);
strb2 = s2.substr(i,lens2-i);
if (isScramble(stra1,strb1)&&isScramble(stra2,strb2))
{
return true;
}
stra1 = s1.substr(0,lens1-i);
stra2 = s1.substr(lens1-i,i);
if (isScramble(stra1,strb2)&&isScramble(stra2,strb1))
{
return true;
}
}
return false;
}
由于判断子串的时候,很多重复,就用动态规划来优化,如何优化呢?这也是一个很棘手的问题。
dp[i][j][k] 代表了s1从i开始,s2从j开始,长度为k的两个substring是否为scramble
string。
有三种情况需要考虑:
1. 如果两个substring相等的话,则为true
2. 如果两个substring中间某一个点,左边的substrings为scramble string,同时右边的substrings也为scramble string,则为true
3. 如果两个substring中间某一个点,s1左边的substring和s2右边的substring为scramble
string, 同时s1右边substring和s2左边的substring也为scramble string,则为true
class Solution {
public:
bool isScramble(string s1, string s2) {
// Start typing your C/C++ solution below
// DO NOT write int main() function
if (s1.length() != s2.length()) {
return false;
}
int length = s1.length();
bool f[length][length][length];
memset(f, false, sizeof(bool) * length * length * length);
for (int k = 1; k <= length; k++) {
for (int i = 0; i <= length - k; i++) {
for (int j = 0; j <= length - k; j++) {
if (k == 1) {
f[i][j][k] = s1[i] == s2[j];
}
else {
for (int l = 1; l < k; l++) {
if ((f[i][j][l] && f[i + l][j + l][k - l]) || (f[i][j + k - l][l] && f[i + l][j][k - l])) {
f[i][j][k] = true;
break;
}
}
}
}
}
}
return f[0][0][length];
}
};