题目
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
思路与解法
这道题目让我们判断是否存在从s1转化为s2的一种可行方法。题目保证输入数据满足s1、s2长度相等。
乍一看,感觉是将s1所有组成字符进行重新排列组合然后将获得的字符串与s2作比较。但是实际上这道题目加了隐藏的“限制”。因为Example 2中abcde
不能转化为caebd
。
下面,简单解释一下,为什么abcde
不能转化为caebd
:由题目可知,我们可以将s1分为两个字串,然后这两个字串可以交换顺序,也可以不交换从而生成新的s1;同样,我们可以对两个字串进行相同的操作。
由此可知,我们从s1进行转化所能得到的字符串是有限的,必须是由上述操作得到的。这是一个十分明显的递归结构,所以我们可以采用递归的方法来判断s1是否可以转化为s2。
如果s1可以转化为s2,那么一定满足以下两个条件之一:
- 不交换子串:s1和s2的前i个字符分别组成的子串可以相互转化,并且s1和s2的后len(s1)-i个字符分别组成的子串可以相互转化;
- 交换子串:s1的前i个字符组成的子串和s2后i个字符组成的子串可以相互转化,并且s1的后len(s1)-i个字符组成的子串和s2后len(s2)-i个字符组成的可以相互转化。
if isScramble(s1[0:i],s2[0:i]) && isScramble(s1[i:], s2[i:])
return true
if isScramble(s1[:i], s2[len(s2)-i:]) && isScramble(s1[i:], s2[:len(s2)-i])
return true
代码实现
此算法我使用go语言实现:
func isScramble(s1 string, s2 string) bool {
// 如果s1==s2直接返回true
if s1 == s2 {
return true
}
// 判断s1和s2中是否存在不相同的字符,避免后续递归的消耗
s1Slice := strings.Split(s1, "")
s2Slice := strings.Split(s2, "")
sort.Strings(s1Slice)
sort.Strings(s2Slice)
if strings.Join(s1Slice, "") != strings.Join(s2Slice, "") {
return false
}
// 递归过程,对应于分析过程中的两种情况
for i:=1; i<len(s1);i++ {
if (isScramble(s1[:i], s2[:i]) && isScramble(s1[i:], s2[i:])) || (isScramble(s1[:i], s2[len(s2)-i:]) && isScramble(s1[i:], s2[:len(s2)-i])) {
return true
}
}
return false
}
遇到的问题
最初并没有判断s1和s2中是否存在不相同的字符,结果造成了无用的递归,消耗了大量的时间,造成了超时:
加入判断之后程序才可以通过,不过排序的时间复杂度为
,所以我们采用下面
复杂度的判断方法:
alphabet := make([]int,26)
for i:=0;i<len(s1);i++ {
alphabet[s1[i]-'a']+=1
alphabet[s2[i]-'a']-=1
}
for i:=0;i<26;i++ {
if alphabet[i] != 0 {return false}
}
return true