利用递归解决LeetCode题库第87题扰乱字符串这个怪题

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

分析:

这个题感觉很怪,具有很大的灵活性,其原因就是因为有两个随机---在随机位置分割字符串以及随机交换位置,使得这个问题好象难以琢磨,无从下手,让我放了很有几天没有去解决。

每天我都会自觉不自觉地去琢磨,通过认真分析,觉得应该这样处理:

  1. 字符串s1和它的扰乱字符串s2,必定具有相同的字符,长度一样,只是字符位置不一样,因此我编写了一个同构字符串函数tgstr(s1,s2),用于检验字符串s1和字符串s2是不是具有这样的特点,如果是同构字符串,返回True,否则返回False
  2. 字符串s1被随机从中间某个位置分割成两个子串x和y,变成同构字符串x+y或y+x,x和y又分别可以同构变化,但不管怎么变化,它们还是具有相对固定的特点,这个固定是说组成x和y的字符不管位置怎么变化,它们必定是同构字符串,具有相同的字符和字符数量,基于这样的特点,就有了下面的处理步骤
  3. 从s1和s2的左端开始向右同步查找,如果不是同构字符串就继续向右查找,直到找到一个同构字符串,然后进行分割,如果找完整个字符串才同构,说明从左到右的找法是找不到分割点的。那么换成s1从左边开始向右找,而s2从右边向左边同步找,如果找到同构字符串,然后进行分割,如果找完整个字符串才同构,也说明中间没有分割点。
  4. 如果两种方法都无法找到分割点且字符长度为1说明就是扰乱字符串,如果长度不为1,说明不是扰乱字符串
  5. 如果找到分割点,则采用递归对分割之后的字符串进行同样处理。为此设计了一个判断函数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

感悟:从算法的角度,用计算思维去观察和思考,你将拥有全新的世界,生活将变得更加美好有趣!

  • 20
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值