leetcode
leetcode 401 ~ 450
编码最短长度的字符串

编码最短长度的字符串

难度:

标签:

题目描述

代码结果

运行时间: 56 ms, 内存: 16.8 MB


/*
 * Problem: Encode the shortest length of a string using Java Streams
 * Idea: Similar to the standard Java solution but using Java Streams for operations where applicable.
 */
import java.util.stream.IntStream;
 
public class EncodeShortestLengthStream {
    public String encode(String s) {
        int n = s.length();
        String[][] dp = new String[n][n];
 
        IntStream.range(1, n + 1).forEach(len -> {
            IntStream.range(0, n - len + 1).forEach(i -> {
                int j = i + len - 1;
                String substr = s.substring(i, j + 1);
                dp[i][j] = substr;
 
                IntStream.range(i, j).forEach(k -> {
                    if ((dp[i][k] + dp[k + 1][j]).length() < dp[i][j].length()) {
                        dp[i][j] = dp[i][k] + dp[k + 1][j];
                    }
                });
 
                String pattern = findPattern(substr);
                if (pattern.length() < substr.length()) {
                    dp[i][j] = pattern;
                }
            });
        });
 
        return dp[0][n - 1];
    }
 
    private String findPattern(String s) {
        int n = s.length();
        return IntStream.rangeClosed(1, n / 2)
                .filter(k -> n % k == 0 && s.replace(s.substring(0, k), "").isEmpty())
                .mapToObj(k -> n / k + "[" + dpEncode(s.substring(0, k)) + "]")
                .findFirst()
                .orElse(s);
    }
 
    private String dpEncode(String s) {
        return encode(s);
    }
}
 

解释

方法:

这个题解采用递归的方式进行字符串压缩。主要思路是将字符串切分成两半,左边一半如果能够按照整倍数压缩,就进行压缩,得到左边一半的最短压缩形式。然后递归处理右边一半,将两半处理完后的字符串拼接起来,其中最短的拼接字符串就是最终的答案。在判断能否进行整倍数压缩时,问题转化为寻找字符串的重复子串。

时间复杂度:

O(n^2)

空间复杂度:

O(n)

代码细节讲解

🦆
题解中提到的字符串压缩算法是否能确保找到全局最短的压缩结果,还是可能只是局部最优解?
这个算法通过递归方式试图找到每个子字符串的最短压缩形式,并在各个级别上尝试所有可能的切分点,理论上可以找到全局最短的压缩结果。然而,因为算法在每一步选择时只考虑当前切分产生的最短结果而不是整体最短,所以在某些情况下可能只达到局部最优。此外,全局最优解的确保还依赖于递归过程中正确的切割和压缩选择,可能需要额外的优化或者更复杂的动态规划方法来确保最短结果。
🦆
递归方法中,为什么选择将字符串平分为两半进行处理?是否有考虑过其他分割方式可能更优?
题解中的算法采用平分的方法进行说明,这是因为平分是一种简单且常见的递归策略,便于理解和实现。实际上,算法并不限于平分,而是尝试了所有可能的切分点,这包括了不同长度的左右切分。通过枚举所有切分点,算法试图找到任何可能的压缩优势,因此已经考虑了多种分割方式,以寻求最优解。
🦆
在寻找重复子串时,你是如何确保找到的是最小的可重复单元,从而实现最大程度的压缩?
在寻找重复子串的过程中,算法利用了字符串自身与其自身拼接(s+s)的方法,并寻找第二个字符串开始出现的位置。这个位置(idx)如果小于字符串的长度(n),表示找到了一个重复的单元。该位置表示最小的可重复单元的长度,从而可以对整个字符串进行压缩。通过这种方式,算法确保了找到的重复单元是最小的,从而最大程度地实现压缩。
🦆
在处理字符串切分和拼接时,有没有可能遇到字符串过长导致性能问题,如果有,如何解决?
在处理较长的字符串时,确实可能遇到性能问题,主要是因为递归的深度过深及切分操作过多导致的计算量大增。为了解决这个问题,可以采用多种策略:1. 使用动态规划替代纯递归,存储已经计算过的结果,避免重复计算。2. 限制递归的深度或者在递归过程中加入剪枝,放弃那些明显不会产生最短结果的路径。3. 在实际应用中,可以考虑使用迭代方法代替递归,或者使用尾递归优化,减少内存消耗。

相关问题

字符串解码

给定一个经过编码的字符串,返回它解码后的字符串。

编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。

你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。

此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。

 

示例 1:

输入:s = "3[a]2[bc]"
输出:"aaabcbc"

示例 2:

输入:s = "3[a2[c]]"
输出:"accaccacc"

示例 3:

输入:s = "2[abc]3[cd]ef"
输出:"abcabccdcdcdef"

示例 4:

输入:s = "abc3[cd]xyz"
输出:"abccdcdcdxyz"

 

提示:

  • 1 <= s.length <= 30
  • s 由小写英文字母、数字和方括号 '[]' 组成
  • s 保证是一个 有效 的输入。
  • s 中所有整数的取值范围为 [1, 300] 

原子的数量

给你一个字符串化学式 formula ,返回 每种原子的数量

原子总是以一个大写字母开始,接着跟随 0 个或任意个小写字母,表示原子的名字。

如果数量大于 1,原子后会跟着数字表示原子的数量。如果数量等于 1 则不会跟数字。

  • 例如,"H2O""H2O2" 是可行的,但 "H1O2" 这个表达是不可行的。

两个化学式连在一起可以构成新的化学式。

  • 例如 "H2O2He3Mg4" 也是化学式。

由括号括起的化学式并佐以数字(可选择性添加)也是化学式。

  • 例如 "(H2O2)""(H2O2)3" 是化学式。

返回所有原子的数量,格式为:第一个(按字典序)原子的名字,跟着它的数量(如果数量大于 1),然后是第二个原子的名字(按字典序),跟着它的数量(如果数量大于 1),以此类推。

 

示例 1:

输入:formula = "H2O"
输出:"H2O"
解释:原子的数量是 {'H': 2, 'O': 1}。

示例 2:

输入:formula = "Mg(OH)2"
输出:"H2MgO2"
解释:原子的数量是 {'H': 2, 'Mg': 1, 'O': 2}。

示例 3:

输入:formula = "K4(ON(SO3)2)2"
输出:"K4N2O14S4"
解释:原子的数量是 {'K': 4, 'N': 2, 'O': 14, 'S': 4}。

 

提示:

  • 1 <= formula.length <= 1000
  • formula 由英文字母、数字、'('')' 组成
  • formula 总是有效的化学式