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. 本文地址
递归解法:s2[0...j]是否可以由s1[0...i]转换 isScramble(s2[0...j], s1[0...i]),可以分解成 i 个子问题(i 其实等于j,因为两个字符串长度不一样,肯定不能互相转换):
( isScramble(s2[0...k], s1[0...k]) && isScramble(s2[k+1...j], s1[k+1...i]) ) || ( isScramble(s2[0...k], s1[i-k...i]) && isScramble(s2[k+1...j], s1[0...i-k-1]) ),(k = 0,1,2 ... i-1,k相当于字符串的分割点)
只要一个子问题返回ture,那么就表示两个字符串可以转换。代码如下:
class Solution {
public:
bool isScramble(string s1, string s2) {
return isScrambleRecur(s1,s2);
}
bool isScrambleRecur(string &s1, string &s2)
{
string s1cop = s1, s2cop = s2;
sort(s1cop.begin(), s1cop.end());
sort(s2cop.begin(), s2cop.end());
if(s1cop != s2cop)return false;//两个字符串所含字母不同
if(s1 == s2)return true;
int len = s1.size();
for(int i = 1; i < len; i++)//分割位置
{
string s1left = s1.substr(0, i);
string s1right = s1.substr(i);
string s2left = s2.substr(0, i);
string s2right = s2.substr(i);
if(isScrambleRecur(s1left, s2left) && isScrambleRecur(s1right, s2right))
return true;
s2left = s2.substr(0, len-i);
s2right = s2.substr(len-i);
if(isScrambleRecur(s1left, s2right) && isScrambleRecur(s1right, s2left))
return true;
}
return false;
}
};
动态规划解法:
递归解法有很多重复子问题,比如s2 = rgeat, s1 = great 当我们选择分割点为0时,要解决子问题 isScramble(reat, geat),再对该子问题选择分割点0时,要解决子问题 isScramble(eat,eat);而当我们第一步选择1作为分割点时,也要解决子问题 isScramble(eat,eat)。相同的子问题isScramble(eat,eat)就要解决2次。本题是我第一次尝试三维的DP,DP编程之前切记要先写出递推公式。
动态规划用数组来保存子问题,设dp[k][i][j]表示s2从j开始长度为k的子串是否可以由s1从i开始长度为k的子串转换而成,那么动态规划方程如下
- 初始条件:dp[1][i][j] = (s1[i] == s2[j] ? true : false)
- dp[k][i][j] = ( dp[divlen][i][j] && dp[k-divlen][i+divlen][j+divlen] ) || ( dp[divlen][i][j+k-divlen] && dp[k-divlen][i+divlen][j] ) (divlen = 1,2,3...k-1, 它表示子串分割点到子串起始端的距离) ,只要一个子问题返回真,就可以停止计算
代码如下:
class Solution {
public:
bool isScramble(string s1, string s2) {
//动态规划解法
if(s1.size() != s2.size())return false;
const int len = s1.size();
//dp[k][i][j]表示s2从j开始长度为k的子串是否可以由s1从i开始长度为k的子串转换而成
bool dp[len+1][len][len];
//初始化长度为1的子串的dp值
for(int i = 0; i <= len-1; i++)
for(int j = 0; j <= len-1; j++)
dp[1][i][j] = s1[i] == s2[j] ? true : false;
for(int k = 2; k <= len; k++)//子串的长度
for(int i = 0; i <= len-k; i++)//s1的起始位置
for(int j = 0; j <= len-k; j++)//s2的起始位置
{
dp[k][i][j] = false;
//divlen表示两个子串分割点到子串起始端的距离
for(int divlen = 1; divlen < k && !dp[k][i][j]; divlen++)
dp[k][i][j] = (dp[divlen][i][j] && dp[k-divlen][i+divlen][j+divlen])
|| (dp[divlen][i][j+k-divlen] && dp[k-divlen][i+divlen][j]);
}
return dp[len][0][0];
}
};
水中的鱼的解法对字符串没有进行排序,而是利用数组来对递归进行剪枝。所以速度稍微快点。
首先想到的是递归,简单明了,对两个string进行partition,然后比较四个字符串段。但是递归的话,这个时间复杂度比较高。然后想到能否DP,但是即使用DP的话,也要O(n^3)。想想算了,还是在递归里做些剪枝,这样就可以避免冗余计算:
- 对于每两个要比较的partition,统计他们字符出现次数,如果不相等返回。
class Solution {
public:
bool isScramble(string s1, string s2) {
// Start typing your C/C++ solution below
// DO NOT write int main() function
if(s1.size() != s2.size()) return false;
int A[256];
memset(A,0,256*sizeof(A[0]));
for(int i =0;i<s1.size(); i++)
{
A[s1[i]-'a']++;
}
for(int i =0;i<s2.size(); i++)
{
A[s2[i]-'a']--;
}
for(int i =0;i<256; i++)
{
if(A[i] !=0)
return false;
}
if(s1.size() ==1 && s2.size() ==1) return true;
for(int i =1; i< s1.size(); i++)
{
bool result= isScramble(s1.substr(0, i), s2.substr(0, i))
&& isScramble(s1.substr(i, s1.size()-i), s2.substr(i, s1.size()-i));
result = result || (isScramble(s1.substr(0, i), s2.substr(s2.size() - i, i))
&& isScramble(s1.substr(i, s1.size()-i), s2.substr(0, s1.size()-i)));
if(result) return true;
}
return false;
}
};