欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

leetcode_87_扰乱字符串

程序员文章站 2022-04-27 08:44:21
...

扰乱字符串

描述

困难

给定一个字符串 s1,我们可以把它递归地分割成两个非空子字符串,从而将其表示为二叉树。

下图是字符串 s1 = “great” 的一种可能的表示形式。

    great
    /    \
    gr    eat
    / \    /  \
    g   r  e   at
               / \
              a   t

在扰乱这个字符串的过程中,我们可以挑选任何一个非叶节点,然后交换它的两个子节点。

例如,如果我们挑选非叶节点 “gr” ,交换它的两个子节点,将会产生扰乱字符串 “rgeat” 。

    rgeat
    /    \
    rg    eat
    / \    /  \
    r   g  e   at
               / \
              a   t

我们将 "rgeat” 称作 “great” 的一个扰乱字符串。

同样地,如果我们继续交换节点 “eat” 和 “at” 的子节点,将会产生另一个新的扰乱字符串 “rgtae” 。

    rgtae
   /    \
  rg    tae
/ \    /  \
r   g  ta  e
      / \
     t   a

我们将 "rgtae” 称作 “great” 的一个扰乱字符串。

给出两个长度相等的字符串 s1 和 s2,判断 s2 是否是 s1 的扰乱字符串。

示例 1:

输入: s1 = "great", s2 = "rgeat"
输出: true

示例 2:

输入: s1 = "abcde", s2 = "caebd"
输出: false

解题

判断扰乱的前期是 s1,s2的长度要相等

然后将s1,s2分别分割成两部分进行判断,分割出的两部分都不为空

该两部分的长度要对应

下图为一个例子

leetcode_87_扰乱字符串

  • 图上第一种情况相当于 s1[:i] -> s2[:i] && s1[i:] -> s2[i:]。s1中前i个字符变为了s2中前i个字符,同时,s1中剩余的字符可以变成s2中剩余的字符
  • 图上第二种情况相当于 s1[:i] -> s2[-i:] && s1[i:] -> s2[:-i] 。s1中前i个字符变成了s2中后i个字符,同时,s1中剩余的字符可以变成s2中剩余的字符

将一个问题分割成了小问题

只需判断最底层,即两个字符时能够变换,如,grrg能否变换

设置dp[i][j][k]的三维动态数组

dp[i][j][k] 表示字符串s1中i开始的长度为k的子串能否变换成s2中j开始的长度为k的子串

最后的目标为d[0][0][len],表示从0开始的两个字符串是否成功能变换

dp[i][j][k]可以根据图上的两种情况分为两种情况

  • dp[i][j][w] && dp[i+w][j+w][k-w] 表示图上第一种情况
  • dp[i][j+k-w] && dp[i+w][j][k-w] 表示图上第二种情况

其中,w是在长度为k的子串中的切分位置,1<=w<=k-1

在初始条件下,k=1,即不能再分割,当s1[i]s2[j]相等时,d[i][j][1]取true,否则为false

class Solution:
    def isScramble(self, s1: str, s2: str) -> bool:
        len1 = len(s1)
        len2 = len(s2)
        # s1 s2 字符串长度需相同
        if len1 != len2:
            return False
        # 最里面的长度为len1+1,用不到dp[i][j][0]
        dp = [[[False for _ in range(len1+1)] for _ in range(len1)] for _ in range(len1)]

        # 初始化  
        for i in range(len1):
            for j in range(len1):
                if s1[i] == s2[j]:
                    dp[i][j][1] = True
        
        # 各区间长度,长度为1的单字符已经初始化了,长度最大为原始字符串的长度
        for k in range(2, len1+1):
            # i的初始位置为0,最后的位置肯定要与len1差k个
            # 不然从i到len1就不足k的长度了
            for i in range(len1-k+1):
                # j也一样 
                for j in range(len1-k+1):
                    # 长度为k的字符串再分割出子串,w为分割线
                    for w in range(1, k):
                        # 第一种情况
                        if dp[i][j][w] and dp[i+w][j+w][k-w]:
                            dp[i][j][k] = True
                            # 满足条件就直接break,表示可以变换,直接进行行下一循环
                            break
                        # 第二种情况
                        if dp[i][j+k-w][w] and dp[i+w][j][k-w]:
                            dp[i][j][k] = True
                            break
        
        return dp[0][0][len1]

当然也有递归

class Solution:
    def isScramble(self, s1: str, s2: str) -> bool:
        # 如果两个字串相同
        if s1 == s2:
            return True
        # 两个子串包含不同的字符
        if sorted(s1) != sorted(s2):
            return False
        for i in range(1, len(s1)):
            # 第一种情况
            if self.isScramble(s1[:i], s2[:i]) and self.isScramble(s1[i:], s2[i:]):
                return True
            # 第二种情况
            if self.isScramble(s1[:i], s2[-i:]) and self.isScramble(s1[i:], s2[:-i]):
                return True
        return False
相关标签: LeetCode 字符串