学号:16340008
Question:
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
Answer:
题目为Dynamic Programming类。题意如下:给定一个字符串,将其以二叉树形式不断分割成两条子串,直到所有叶子节点都是单个字母。问能否通过交换若干个非叶子节点的两个子节点,使所有叶子节点按顺序排列为。对于函数,当与符合上面条件时,返回。
设与为的树根的两个子节点,与为的两个子节点。
我们可以推断,若为,则必定
①有与使为 且 有与使为
或
②有与使为 且 有与使为(即S1L与S1L交换后为①)
因此我们可以把问题变为检查与的子串是否互为scrambled string。这里的动态规划十分有分治算法的影子,可以用递归实现。
算法步骤如下:
- 检查与是否相同,相同则返回真。
- 检查与是否等长,不等长则返回假。
- 检查与所含字母是否相同,这是他们能返回真的前提,不相同则返回假
- 执行步骤5与6。
- 分别将与分割成两个子串(与长度相等,右子串同理),将与作为与执行步骤1,将与作为与执行步骤1,两次调用的返回值相与后返回
- 分别将与分割成两个子串(与长度相等,右子串同理),将与作为与执行步骤1,将与作为与执行步骤1,两次调用的返回值相与后返回
- 若步骤5或步骤6的返回值为真,则返回真,否则再次按不同长度分割执行步骤5,6。
得到代码如下(python3):
class Solution:
def isScramble(self, s1, s2):
"""
:type s1: str
:type s2: str
:rtype: bool
"""
if (s1 == s2):
return True
if (len(s1) != len(s2)):
return False
if (sorted(s1) != sorted(s2)):
return False
length = len(s1)
for i in range(length - 1):
if self.isScramble(s1[: i + 1], s2[: i + 1]) and self.isScramble(s1[i + 1:], s2[i + 1:]):
return True
if self.isScramble(s1[: i + 1], s2[length - 1 - i:]) and self.isScramble(s1[i + 1:], s2[: length - 1 - i]):
return True
return False
本地测试代码:
s1 = "abc"
s2 = "bca"
test = Solution()
print(test.isScramble(s1, s2))
运行结果:
然而这个算法有一个可以优化的点。上面的代码相当于一个多叉树,从根部向下发散,得到返回值则往上层传递。实际上有节点是重复的。因此当一个与符合条件时,我们可以将其添加到一个集合中,然后在函数中添加检查与是否在集合中来避开其子节点的发散递归。
改进代码:
class Solution:
def isScramble(self, s1, s2):
"""
:type s1: str
:type s2: str
:rtype: bool
"""
S = set()
if (s1 == s2):
return True
if (len(s1) != len(s2)):
return False
if (sorted(s1) != sorted(s2)):
return False
if (s1, s2) in S:
return True
length = len(s1)
for i in range(length - 1):
if self.isScramble(s1[: i + 1], s2[: i + 1]) and self.isScramble(s1[i + 1:], s2[i + 1:]):
S.add((s1,s2))
S.add((s2,s1))
return True
if self.isScramble(s1[: i + 1], s2[length - 1 - i:]) and self.isScramble(s1[i + 1:], s2[: length - 1 - i]):
S.add((s1,s2))
S.add((s2,s1))
return True
return False
结果如下:
然而这个结果似乎会受网站后台的状况有一定浮动,改进前的代码两次提交,一次能达到50ms一次会去到100ms。我们姑且相信这次代码对运算有改进。