题目链接: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.
这道题的要求是判断两个字符串是否是Scramble String。
如果两个字符串是Scramble String,则其长度必然相等。而且,在某位置切开s1,则s1左侧与s2左侧相同数量的子串为Scramble String,并且s1右侧与s2右侧相同数量的子串为Scramble String;或者s1左侧与s2右侧相同数量的子串为Scramble String,并且s1右侧与s2左侧相同数量的子串为Scramble String。
1. 递归处理
思路有了,就可以通过递归调用遍历每组分割点了。其中a1和b1分别表示s1中以a1开始b1结束的子串,a2和b2分别表示s2中以a2开始b2结束的子串。
1 bool isScramble(string s1, int a1, int b1, string s2, int a2, int b2)
2 {
3 if(b1 - a1 != b2 - a2)
4 return false;
5 if(a1 == b1)
6 return s1[a1] == s2[a2];
7
8 for(int i = a1; i < b1; ++ i)
9 {
10 if(isScramble(s1, a1, i, s2, a2, a2 + i - a1) &&
11 isScramble(s1, i + 1, b1, s2, b2 - (b1 - i - 1), b2))
12 return true;
13 if(isScramble(s1, a1, i, s2, b2 - (i - a1), b2) &&
14 isScramble(s1, i + 1, b1, s2, a2, a2 + b1 - i - 1))
15 return true;
16 }
17
18 return false;
19 }
遗憾的是,超时。。。仔细想一下,还可以进行剪枝:如果s1和s2的子串中的字符不相同,则其必然不是Scramble String。
顺利AC。
时间复杂度:O(???)
空间复杂度:O(1)
1 class Solution
2 {
3 public:
4 bool isScramble(string s1, string s2)
5 {
6 return isScramble(s1, 0, s1.size() - 1, s2, 0, s2.size() - 1);
7 }
8 private:
9 bool isScramble(string s1, int a1, int b1, string s2, int a2, int b2)
10 {
11 if(b1 - a1 != b2 - a2)
12 return false;
13 if(a1 == b1)
14 return s1[a1] == s2[a2];
15
16 // 剪枝,如果子串中的字符不同,则必然不是Scramble String,不用再进行分割处理了
17 int cnt[256] = {0};
18 for(int i = a1; i <= b1; ++ i)
19 ++ cnt[s1[i]];
20 for(int i = a2; i <= b2; ++ i)
21 -- cnt[s2[i]];
22 for(int i = 0; i < 256 ;++ i)
23 if(cnt[i] != 0)
24 return false;
25
26 for(int i = a1; i < b1; ++ i)
27 {
28 if(isScramble(s1, a1, i, s2, a2, a2 + i - a1) &&
29 isScramble(s1, i + 1, b1, s2, b2 - (b1 - i - 1), b2))
30 return true;
31 if(isScramble(s1, a1, i, s2, b2 - (i - a1), b2) &&
32 isScramble(s1, i + 1, b1, s2, a2, a2 + b1 - i - 1))
33 return true;
34 }
35
36 return false;
37 }
38 };
2. 动态规划
维护变量dp[i][j][len],其中i是s1的起始字符,j是s2的起始字符,而n是当前的字符串长度,表示的是以i和j分别为s1和s2起点的长度为len的字符串是不是互为Scramble String。
递推公式也是前面的思路:dp[i][j][len] ||= (dp[i][j][k]&&dp[i+k][j+k][len-k] || dp[i][j+len-k][k]&&dp[i+k][j][len-k])。
参考自这里。
时间复杂度:O(???)
空间复杂度:O(1)
1 class Solution
2 {
3 public:
4 bool isScramble(string s1, string s2)
5 {
6 if(s1.size() == 0)
7 return true;
8 // dp[i][j][len]表示的是以i和j分别为s1和s2起点的长度为len的字符串是不是互为Scramble String
9 vector<vector<vector<bool> > > dp(s1.size(),
10 vector<vector<bool> >(s2.size(),
11 vector<bool>(s1.size() + 1, false)));
12 for(int i = 0; i < s1.size(); ++ i)
13 for(int j = 0; j < s2.size(); ++ j)
14 dp[i][j][1] = s1[i] == s2[j];
15
16 for(int len = 2; len <= s1.size(); ++ len)
17 for(int i = 0; i < s1.size() - len + 1; ++ i)
18 for(int j = 0; j < s2.size() - len + 1; ++ j)
19 for(int k = 1; k < len; ++ k)
20 dp[i][j][len] = dp[i][j][len] ||
21 dp[i][j][k] && dp[i + k][j + k][len - k] ||
22 dp[i][j + len - k][k] && dp[i + k][j][len - k];
23
24 return dp[0][0][s1.size()];
25 }
26 };