题目
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.
Example 1:
Input: s1 = "great", s2 = "rgeat" Output: true
Example 2:
Input: s1 = "abcde", s2 = "caebd" Output: false
题目分析以及解决思路
- 题目的意思也比较明确吧,给出两个字符串,将字符串分割成一个二叉树,并且可以交换非叶子节点,求解两个字符串能否相互构造。
方法 1 - 递归
- 思路:将字符串划分为两部分,分别递归得到结果是否
scramble
。需要考虑完备性,即需要将各种划分情况都需要考虑一遍。- 情况①:
s1
的前k
个字符与s2
的前k
个字符进行判断同时s1
的后size-k
个字符与s2
的后size-k
进行判断 —isScramble(s1.substr(0, k), s2.substr(0, k)) && isScramble(s1.substr(k, size-k), s2.substr(k, size-k))
- 情况②:
s1
的前k
个字符与s2
的后k
个字符进行判断同时s1
的后size-k
个字符与s2
的前size-k
进行判断 —isScramble(s1.substr(0, size-k), s2.substr(k, size-k)) && isScramble(s1.substr(size-k, k), s2.substr(0, k))
- 情况①:
- 实现代码:
- 如果不剪枝将会造成超时
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30// 递归
class Solution {
public:
bool isScramble(string s1, string s2) {
if(s1 == s2) return true;
if(s1.size() != s2.size()) return false;
int size = s1.size();
// 剪枝
int letter[26] = {0};
for(int i = 0; i < size; ++i) {
letter[s1[i] - 'a'] ++;
letter[s2[i] - 'a'] --;
}
for(int i = 0; i < 26; ++i) {
if(letter[i] > 0)
return false;
}
for(int k = 1; k < size; ++k) {
if(isScramble(s1.substr(0, k), s2.substr(0, k)) && isScramble(s1.substr(k, size-k), s2.substr(k, size-k)))
return true;
if(isScramble(s1.substr(0, size-k), s2.substr(k, size-k)) && isScramble(s1.substr(size-k, k), s2.substr(0, k)))
return true;
}
return false;
}
};方法 2 -
动态规划
- 如果不剪枝将会造成超时
- 我们需要维护一个变量
match[i][j][n]
,其中i
为字符串s1
的起始字符,j
为字符串s2
的起始字符,n
为当前字符的长度,match[i][j][len]
表示的是以i
和j
分别为s1
和s2
起点的长度为len
的字符串是不是互为scramble
。 - 如何得到
match[i][j][n]
,推导出其中的状态转移方程,其实与递归中的思路相同。我们对于当前情况,需要对于目前所有可能的划分都去检索一次,如果有一次划分是scramble
的,则match[i][j][len]
即为true
,倘若所有的划分都不是scramble
的,则match[i][j][len]
即为false
。关于划分,同时需要划分两种情况:- 情况①:
s1
以i
为起始的前k
个字符与s2
以j
为起始的前k
个字符进行判断同时s1
的后len-k
个字符与s2
的后len-k
进行判断 —match[i][j][k] && match[i+k][j+k][len-k]
- 情况②:
s1
以i
为起始的k
个字符与s2
以j+len-k
为起始的k
个字符进行判断同时s1
以i+k
为起始的len-k
个字符与s2
以j
为起始的len-k
进行判断 —match[i][j+len-k][k] && match[i+k][j][len-k]
- 情况①:
- 实现代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30// dp
class Solution {
public:
bool isScramble(string s1, string s2) {
if(s1.size() != s2.size()) return false;
if(!s1.size()) return true;
int size = s1.size();
// s1_start, s2_start, match_len
bool match[size][size][size+1];
for(int i = 0; i < size; ++i)
for(int j = 0; j < size; ++j)
match[i][j][1] = (s1[i] == s2[j]);
for(int len = 2; len <= size; ++len)
for(int i = 0; i <= size - len; ++i)
for(int j = 0; j <= size - len; ++j) {
match[i][j][len] = false; // initial
for(int k = 1; k < len; ++k)
match[i][j][len] |= (match[i][j][k] && match[i+k][j+k][len-k]) \
|| (match[i][j+len-k][k] && match[i+k][j][len-k]);
}
return match[0][0][size];
}
};