87. 扰乱字符串
难度:困难
使用下面描述的算法可以扰乱字符串 s 得到字符串 t :
如果字符串的长度为 1 ,算法停止
如果字符串的长度 > 1 ,执行下述步骤:
在一个随机下标处将字符串分割成两个非空的子字符串。
即,如果已知字符串 s ,则可以将其分成两个子字符串
x 和 y ,且满足 s = x + y 。
随机 决定是要「交换两个子字符串」还是要「保持这两
个子字符串的顺序不变」。即,在执行这一步骤之后,s
可能是 s = x + y 或者 s = y + x 。
在 x 和 y 这两个子字符串上继续从步骤 1 开始递归执行此算法。
给你两个 长度相等 的字符串 s1 和 s2,判断 s2 是否是 s1 的
扰乱字符串。如果是,返回 true ;否则,返回 false 。
示例 1:
输入:s1 = "great", s2 = "rgeat"
输出:true
解释:s1 上可能发生的一种情形是:
"great" --> "gr/eat" // 在一个随机下标处分割得到两个子字符串
"gr/eat" --> "gr/eat" // 随机决定:「保持这两个子字符串的顺序不变」
"gr/eat" --> "g/r / e/at" // 在子字符串上递归执行此算法。两个子字符串分别在随机下标处进行一轮分割
"g/r / e/at" --> "r/g / e/at" // 随机决定:第一组「交换两个子字符串」,第二组「保持这两个子字符串的顺序不变」
"r/g / e/at" --> "r/g / e/ a/t" // 继续递归执行此算法,将 "at" 分割得到 "a/t"
"r/g / e/ a/t" --> "r/g / e/ a/t" // 随机决定:「保持这两个子字符串的顺序不变」
算法终止,结果字符串和 s2 相同,都是 "rgeat"
这是一种能够扰乱 s1 得到 s2 的情形,可以认为 s2 是 s1 的扰乱字符串,返回 true
示例 2:
输入:s1 = "abcde", s2 = "caebd"
输出:false
示例 3:
输入:s1 = "a", s2 = "a"
输出:true
分析:
这个题感觉很怪,具有很大的灵活性,其原因就是因为有两个随机---在随机位置分割字符串以及随机交换位置,使得这个问题好象难以琢磨,无从下手,让我放了很有几天没有去解决。
每天我都会自觉不自觉地去琢磨,通过认真分析,觉得应该这样处理:
- 字符串s1和它的扰乱字符串s2,必定具有相同的字符,长度一样,只是字符位置不一样,因此我编写了一个同构字符串函数tgstr(s1,s2),用于检验字符串s1和字符串s2是不是具有这样的特点,如果是同构字符串,返回True,否则返回False
- 字符串s1被随机从中间某个位置分割成两个子串x和y,变成同构字符串x+y或y+x,x和y又分别可以同构变化,但不管怎么变化,它们还是具有相对固定的特点,这个固定是说组成x和y的字符不管位置怎么变化,它们必定是同构字符串,具有相同的字符和字符数量,基于这样的特点,就有了下面的处理步骤
- 从s1和s2的左端开始向右同步查找,如果不是同构字符串就继续向右查找,直到找到一个同构字符串,然后进行分割,如果找完整个字符串才同构,说明从左到右的找法是找不到分割点的。那么换成s1从左边开始向右找,而s2从右边向左边同步找,如果找到同构字符串,然后进行分割,如果找完整个字符串才同构,也说明中间没有分割点。
- 如果两种方法都无法找到分割点且字符长度为1说明就是扰乱字符串,如果长度不为1,说明不是扰乱字符串
- 如果找到分割点,则采用递归对分割之后的字符串进行同样处理。为此设计了一个判断函数pd(s1,s2),通过对其调用就可以确定s2是不是s1的扰乱字符串。
程序如下:
#判断两个字符串s1和s2是否为同构字符串,所谓同构字符串即有相同的字符及个数,但位置不一致
def tgstr(s1,s2):
for c in s1:
if c in s2:
continue
else:
return False
else:
if len(s1)==len(s2):
return True
else:
return False
#判断是否从某处分隔开,如果是则返回s1、s2分割的两对同构字符串并递归调用
def pd(s1,s2):
#注意s1和s2必须是同构字符串才可以
if not tgstr(s1,s2):
return False
#f返回从左向右同步查找找到的分割点的位置,如果查找完整个字符串后才同构,返回-1
f=0
#g返回s1从左到右,而s2从右向左查找找到的分割点的位置,如果查找完整个字符串后才同构,返回-1
g=0
for i in range(len(s1)):
t1=s1[:i+1]
t2=s2[:i+1]
if not tgstr(t1,t2):
continue
else:
f=i if i!=len(s1)-1 else -1
break
for i in range(len(s1)):
t1=s1[:i+1]
w2=s2[-(i+1):]
#print(t1,w2)
if not tgstr(t1,w2):
continue
else:
g=i if i!=len(s1)-1 else -1
break
#对查找的情况进行判断,以决定是否递归处理
if f==-1 and g==-1 and len(s1)!=1:
return False
elif f==-1 and g==-1 and len(s1)==1:
return True
elif f!=-1 and g==-1:
print([s1[:f+1],s2[:f+1]],[s1[f+1:],s2[f+1:]])
return pd(s1[:f+1],s2[:f+1]) and pd(s1[f+1:],s2[f+1:])
else:
print([s1[:g+1],s2[-(g+1):]],[s1[g+1:],s2[:-(g+1)]])
return pd(s1[:g+1],s2[-(g+1):]) and pd(s1[g+1:],s2[:-(g+1)])
#输入s1和s2并打印s2是否是s1的扰乱字符串
s1=input('pls input s1=')
s2=input('pls input s2=')
print(pd(s1,s2))
运行实例1:
pls input s1=abcde
pls input s2=abcde
['a', 'a'] ['bcde', 'bcde']
['b', 'b'] ['cde', 'cde']
['c', 'c'] ['de', 'de']
['d', 'd'] ['e', 'e']
True
运行实例2:
pls input s1=abcdef
pls input s2=acdefb
['a', 'a'] ['bcdef', 'cdefb']
['b', 'b'] ['cdef', 'cdef']
['c', 'c'] ['def', 'def']
['d', 'd'] ['ef', 'ef']
['e', 'e'] ['f', 'f']
True
运行实例3:
pls input s1=great
pls input s2=rgeat
['gr', 'rg'] ['eat', 'eat']
['g', 'g'] ['r', 'r']
['e', 'e'] ['at', 'at']
['a', 'a'] ['t', 't']
True
运行实例5:
pls input s1=abcde
pls input s2=caebd
False
感悟:从算法的角度,用计算思维去观察和思考,你将拥有全新的世界,生活将变得更加美好有趣!