Leetcode力扣秋招刷题路-0087

文章介绍了如何判断两个字符串是否为扰乱字符串,即按照特定的分治算法进行随机交换后能否得到另一个字符串。解题思路包括递归和动态规划,递归方法由于重复计算效率低下,因此采用动态规划优化,避免了时间复杂度为N!的情况。最终给出的Java代码实现了动态规划的解决方案,具有O(n^4)的时间复杂度和O(n^3)的空间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

从0开始的秋招刷题路,记录下所刷每道题的题解,帮助自己回顾总结

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.length == s2.length
1 <= s1.length <= 30
s1 和 s2 由小写英文字母组成

解题思路
我们先来思考两个字符串S和T在什么情况下是扰乱字符串: 首先,这两个字符串长度要想等, 其次,这两个字符串组成要一样 然后,S和T中,至少存在一个i,使得S的前半段和T的前半段扰动,S后半段和T后半段扰动;或者S前半段和T后半段扰动,S后半段和T前半段扰动; 翻译成形式语言即使: 1.length(S)==length(T) 2.sort(s)==sort(T) 3.至少存在一个整数i在[1,length(S)],使得(~符号表示是扰动字符串): S(0:i)~T(0:i) && S(i:end)~T(i:end) or S(0:i)~T(end-i:end) && S(end-i:end)~T(0:i)

这很容想到用递归的思想去做,但我们遇到S和T的时候,递归的判断是否存在这样的i就行了。 下面给出递归的伪代码:

isScramble(S,T){
    /*
    判断1,2条件:
    1.length(S)==length(T)
    2.sort(s)==sort(T)
    */
    end=length(S)
    for(i=1;i<=length(S);i++){
        if(isScramble(S(0:i),T(0:i)) && isScramble(S(i:end),T(i:end)))
            return true
        if(isScramble(S(0:i),T(end-i:end)) && isScramble(S(end-i:end),T(0:i)))
            return true

    }
}

递归的思路非常清晰,自顶向下,但是有很多重复计算,时间复杂度是N!,几乎我们碰到N!复杂度的时候就知道解法失效。 为了避免重复计算,我们只需要把递归改成动态规划,用数组来保存计算过的内容 把自顶向下改成自底向上很重要的地方是如何找到最小的低,即是先计算什么 从上面的递归思路来看,最下面(里面)的是对T和S进行切片计算, 并且显然是片段越短计算越简单 由此我们先从最小切片开始计算 用l表示切片的长度,逐步增加 我们用dp[i][j][L]表示S(i:i+L)和T(j:j+L)两个子串是否扰动 代码如下:

代码

class Solution {
    public boolean isScramble(String s1, String s2) {
        if(s1.length()!=s2.length())
            return false;
        if(s1.length()==0)
            return false;
        boolean[][][] dp = new boolean[s1.length()][s2.length()][s1.length()+1];
        int n=s1.length();
        for(int i=0;i<n;i++){//切片l是1的时候直接判断两个字符是否相等就好
            for(int j=0;j<n;j++){
                dp[i][j][1]= s1.charAt(i)==s2.charAt(j);
            }
        }
        for(int l=2; l<=n;l++)//切片从小到大
            for(int i=0;i<n-l+1;i++)
                for(int j=0;j<n-l+1;j++){//依次遍历S、T
                    for(int k=1;k<l;k++){//对长度为l的S(i:i+L)和T(j:j+L)字符串模仿递归进行切片判断
                        if(dp[i][j][k]&&dp[i+k][j+k][l-k]){
                            dp[i][j][l]=true;
                            break;
                        }
                            
                        if(dp[i][j+l-k][k]&&dp[i+k][j][l-k]){
                                dp[i][j][l]=true;
                                break;
                        }
                            
                    }
                }
        return dp[0][0][n];
    }
}

时间复杂度 O ( n 4 ) O(n^4) O(n4) 空间复杂度 O ( n 3 ) O(n^3) O(n3)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

fffffffyy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值