leetcode刷题日记-【87. 扰乱字符串】

85 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第29天,点击查看活动详情

题目描述

使用下面描述的算法可以扰乱字符串 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 由小写英文字母组成

题目元素

给定两个字符串,s1、s2。s1可以通过s2扰乱得到。扰乱的定义:将字符串随机分成两个不为空的字符串,将这两个字符串交换顺序或者保持原来顺序得到新字符串,这个过程就叫扰乱。

解题思路

根据题意扰乱是具有可反性的,即如果字符串s2可以通过s1扰乱得到,那么s2也可以通过s1扰乱得到。

将这个问题进一步拆分,如果s1可以通过s2扰乱得到,那么必定存在将s1分割n次的字符串也可以通过s2分割成的n个字符串通过扰乱得到。所以这道题可以使用动态规划来解题。

首先确定状态,假设s1长度为n,要求s1是否能通过s2通过扰乱得到,则要求的最原始的问题是从s1字符串索引为0的位置一直到索引为n的位置都是可以通过扰乱得到的;

进一步分解,假设从s1中任意取一个位置为i,分割成2个字符串s1(0,i),s2(i,n-i);(括号内第一个元素代表开始索引,第二个元素代表当前字符串长度。);

因为分解后的字符串可以有两种扰乱方式;

第一种是不交换位置,即将s2进行相同位置的分割s1(0,i)->s2(0,i),s1(i,n-i)->s2(i,n-i)。分割后的字符串必须一样是互相可以扰乱得到的;

第二种是交换位置,即s2前后两个字符串的位置分别对应s1的后前位置,s1(0,i)->s2(n-i,i);s1(i,n-i)->s2(0,n-i);

由此推理出动态规划的状态:f(s1,s2),代表字符串s1,s2是否能通过扰动互相获取

动态方程式

交换位置f(s1,s2) = f(s1(0,i),s2(n-i,i))&f(s1(i,n-i),s2(0,n-i));只要找到一个满足条件的i就直接返回true

不交换位置f(s1,s2) = f(s1(0,i),s2(0,i))&f(s1(i,n-i),s2(i,n-i));只要找到一个满足条件的i就直接返回true

初始条件

s1,s2长度相等,所以当s1中字母出现次数和s2中字母出现次数不相等时,f(s1,s2) = false

代码实现

public static List<List<Integer>> groupThePeople(int[] groupSizes) {
    List<List<Integer>> list = new ArrayList<>();
    // 创建每个数字对应的元素列表
    HashMap<Integer, List<Integer>> timesMap = new HashMap<>();
    for (int i = 0;i < groupSizes.length;i++) {
        int size = groupSizes[i];
        // 数组大小对应相应元素的数组数组
        timesMap.putIfAbsent(size,new ArrayList<>());
        // 将当前元素放入map中
        timesMap.get(size).add(i);
    }
    // 组装数组数据
    timesMap.forEach((num,items)-> {
        // 数组内有元素
        // 得到的是组数
        int groupNum = items.size() / num;
        for (int i=0;i < groupNum ; i++) {
            // 从item里面取
            List<Integer> eachGroup = new ArrayList<>();
            for (int j =0;j < num;j++) {
                // 对应item的下标
                eachGroup.add(items.get(num*i+j));
            }
            list.add(eachGroup);
        }
    });
    return list;
}