leetcode 87. Scramble String(扰乱字符串)

在这里插入图片描述

scramble(字符串s) 算法:
s长度为1时结束。
s可以拆分成2部分x和y,s=x+y,
这两部分可以交换,也可以不交换,即 s = x+y 或 s = y+x.
上面scramble还会递归作用于x 和 y.

给出相同长度的字符串s1, s2, 问s2是否可通过scramble(s1)获得。

思路:

假设s1和s2的长度是n.

在scramble算法中,第1,字符串可以拆分成两部分
那么可以拆分的点可以在任意下标处,
所以需要遍历s1的所有下标。
假设遍历下标为 i 时,s 拆分成 0 ~ i 和 i+1 ~ n-1两段。所以 i : 0 ~ n-2 (不是n-1 !)

第2,拆分的两部分 可交换可不交换
假设在 i 处把s1分成两部分: s1[0 ~ i] 和 s1[i+1 ~ n-1] ,
如果不交换,需要进一步scramble s1[0 ~ i] 和 s2[0 ~ i], s1[i+1 ~ n-1] 和 s2[i+1 ~ n-1].
如果交换,需要进一步scramble s1[0 ~ i] 和 s2[i ~ n-1], s1[i+1 ~ n-1] 和 s2[0 ~ i-1].

同时,我们观察到,
s1能scramble成s2的前提是 它们的字母必须是一样的(不管顺序)。
因为scramble只负责拆分和交换,并不会改变字母,
如果字母不一样,则s1 scamble不成s2的.

所以可以用一个长度为26的数组统计每个字母出现的次数,
因为涉及到交换和不交换,
所以分别用一个cnt统计s1[0 ~ i]的字母,
cntF统计s2[0 ~ i]的字母,cntB统计s2[i+1 ~ n-1]的字母。
字母个数相同时,再进行scamble算法。

两个字符串相等时结束
已经判断过的,用一个map保存起来,再遇到同样的s1和s2, 直接取结果。

注意s必须是拆分成2部分,因此 i < n-1, 而不是 i < n,如果 i = n-1, 会相当于只拆分成一部分(即s1本身,会陷入无限循环)。

class Solution {
    HashMap<String, Boolean> map = new HashMap<>();
    
    public boolean isScramble(String s1, String s2) {
        String s = s1 + s2;
        //相当于DP,记录已经判断过的s1和s2
        if(!map.containsKey(s)) map.put(s, helper(s1, s2));
        return map.get(s);    
    }

    boolean helper(String s1, String s2) {
        if(s1.equals(s2)) return true;

        int[] cnt = new int[26];  //s1从前往后字母计数
        int[] cntF = new int[26]; //s2从前往后字母计数,cntForward
        int[] cntB = new int[26]; //s2从后往前字母计数,cntBackward

        for(int i = 0; i < s1.length()-1; i++) {  //遍历所有的分割点
            int j = s2.length()-1-i;
            cnt[s1.charAt(i)-'a']++;   //统计s1的0~i段字母个数
            cntF[s2.charAt(i)-'a']++;  //统计s2的0~i段(前半段)字母个数
            cntB[s2.charAt(j)-'a']++;  //统计s2的i~n-1段(后半段)字母个数

            if(Arrays.equals(cnt, cntF)) {  //s2的0~i段与s1的0~i段是否字母个数一致(相当于不swap)
                if(isScramble(s1.substring(0,i+1), s2.substring(0,i+1)) &&
                isScramble(s1.substring(i+1), s2.substring(i+1))) return true;
            }
            //不要用else if!不然会漏掉交换后的验证
            if(Arrays.equals(cnt, cntB)){  //s2的i~n-1段与s1的0~i段是否字母个数一致(相当于swap)
                if(isScramble(s1.substring(0,i+1), s2.substring(j)) && 
                isScramble(s1.substring(i+1), s2.substring(0,j))) return true;
            }
        }
        return false;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

蓝羽飞鸟

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

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

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

打赏作者

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

抵扣说明:

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

余额充值