Scramble String
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.
解题思路:
法1:递归 + 剪枝
剪枝:
1. 长度不等,返回false,剪掉;
2. 两个string完成相同,返回true,剪掉;
3. 统计每个字母的个数,不等,则返回false,剪掉。
递归:关键任务是确定分割点。暴力破解,遍历从1~len-1的分割点
每个分割点有两种可能,翻转与不翻转,分别递归
源码:
class Solution {
public:
bool isScramble(string s1, string s2) {
// 递归 + 剪枝
// 剪枝:统计每个字母的个数,不等 则返回
// 递归:关键任务是确定分割点。暴力破解,遍历从1~len-1的分割点
// 每个分割点有两种可能,翻转与不翻转
if (s1.size() != s2.size())
return false;
if (s1 == s2) // 没有这个剪枝,大数据量过不了
return true;
int vcnt[26] = {0};
for (int i = 0; i < s1.size(); ++i)
++vcnt[s1[i] - 'a']; // 考虑小写字母
for (int i = 0; i < s2.size(); ++i)
--vcnt[s2[i] - 'a'];
for (int i = 0; i < 26; ++i)
if (vcnt[i] != 0)
return false;
for (int i = 1; i < s1.size(); ++i)
if ((isScramble(s1.substr(0, i), s2.substr(0, i)) &&
isScramble(s1.substr(i), s2.substr(i))) || // 先检查不翻转的情况
(isScramble(s1.substr(0, i), s2.substr(s2.size()-i)) &&
isScramble(s1.substr(i), s2.substr(0, s2.size()-i))) ) // 再检查左右翻转的情况
return true;
return false;
}
};
法2:动态规划
\ (f[i][j][l] && f[i+l][j+l][k-l]) || ((f[i][j+k-l][l] && f[i+l][j][k-l]))
时间复杂度O(n^4)
class Solution {
public:
bool isScramble(string s1, string s2) {
// 法2:动态规划
// f[i][j][k] 表示 s1从i开始k个字符 与 s2从j开始k个字符 是否为 scrambled string
// / (s1[i] == s2[j]), k=1
// f[i][j][k] = 枚举分割点l(1 <= l <= k-1),分左右不翻转和翻转两种情况讨论
// \ (f[i][j][l] && f[i+l][j+l][k-l]) || ((f[i][j+k-l][l] && f[i+l][j][k-l]))
// 时间复杂度O(n^4)
if (s1.size() != s2.size())
return false;
if (s1 == s2)
return true;
int n = s1.size();
vector<vector<vector<bool> > > f(n, vector<vector<bool> >(n, vector<bool>(n+1, false)));
for (int k = 1; k <= n; ++k) // 为方便,k从1开始,相应的第一维分配空间为 n+1
{
for (int i = 0; i <= n-k; ++i) // 注意k限制了i的取值
{
for (int j = 0; j <= n-k; ++j)
{
if (k == 1)
f[i][j][1] = (s1[i] == s2[j]);
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;
} // else false
}
}
}
}
return f[0][0][n];
}
};