leetcode
leetcode 2101 ~ 2150
将字符串分割成值不超过 K 的子字符串

将字符串分割成值不超过 K 的子字符串

难度:

标签:

题目描述

代码结果

运行时间: 44 ms, 内存: 16.5 MB


/*
 * 思路:
 * 使用Java Stream的方式来实现同样的逻辑,避免使用嵌套循环。
 * 我们仍然需要动态规划数组dp来存储最小分割数。
 */

import java.util.stream.IntStream;

public class GoodSplitStream {
    public int minimumGoodSplit(String s, int k) {
        int n = s.length();
        int[] dp = new int[n + 1];
        Arrays.fill(dp, Integer.MAX_VALUE);
        dp[0] = 0;

        IntStream.rangeClosed(1, n).forEach(j -> {
            IntStream.rangeClosed(1, j).map(i -> j - i + 1).forEach(i -> {
                String sub = s.substring(i - 1, j);
                if (Long.parseLong(sub) <= k) {
                    dp[j] = Math.min(dp[j], dp[i - 1] + 1);
                }
            });
        });

        return dp[n] == Integer.MAX_VALUE ? -1 : dp[n];
    }
}

解释

方法:

此题解采用了贪心算法,目标是最小化分割子字符串的数量,同时每个子字符串的整数值不超过给定的k。首先,根据k的大小决定最大可能的子字符串长度(siz),这是通过计算k可以有多少位数来确定的。例如,如果k是60,则最大可能的子字符串长度是2,因为60是两位数。接着,根据siz尝试从字符串s的左侧开始尽可能地切分长度为siz的子字符串,同时确保每个子字符串的数值不超过k。如果在某个位置上无法进行这样的切分,尝试减少一个字符,使长度变为siz-1,再次检查是否满足条件。如果仍不满足条件,则返回-1,表示无法进行好分割。这个过程重复直到字符串的末尾。

时间复杂度:

O(n)

空间复杂度:

O(1)

代码细节讲解

🦆
为什么在计算最大可能的子字符串长度时,仅考虑了k可以有多少位数,而没有考虑数字序列的具体组合?
在设计贪心算法时,我们首先通过确定k可以表示为多少位数来估算可能的子字符串的最大长度。这种方法简化了问题,因为如果我们考虑每个可能的数字组合,复杂度将显著增加。此外,只有当子字符串的长度不超过k的位数时,其数值才有可能不超过k。这是一种保守的估计,旨在快速确定一个安全的上界,从而避免不必要的复杂性。
🦆
当k的位数为1时,解法中直接对字符串s中的每个数字进行比较,这种方法是否在所有情况下都有效?
当k的位数为1时,意味着k的值从0到9。这种情况下,字符串s中的每个字符应该是一个0到9的数字,且每个字符独立为一个子字符串。只要检查每个字符是否不超过k即可。因为每个字符都是独立的数字,所以这种方法在所有情况下都是有效的,前提是s只包含数字字符。
🦆
在尝试切分长度为siz的子字符串时,如果子字符串的起始位置加上siz超过了字符串s的长度,该如何处理?
如果子字符串的起始位置加上siz超过了字符串s的长度,此时不能形成一个完整的siz长度的子字符串。在这种情况下,我们应该考虑从当前位置到字符串结尾的所有字符作为一个子字符串,并检查其值是否不超过k。如果超过k,则无法进行有效的分割,并应返回-1。否则,这将是最后一个子字符串,并且分割结束。
🦆
解法提到如果无法进行切分,则尝试减少一个字符,这种策略是否可能导致漏掉某些有效的分割方式?
尝试减少一个字符的策略基于贪心算法的思想,即尽可能地使用最长的子字符串来减少总的分割次数。虽然这种策略可能在某些情况下未能找到所有可能的分割方式,但它保证了如果存在一种有效的分割方法,该方法将最小化分割数量。如果减少字符后仍不满足条件,则可能需要更复杂的回溯或动态规划方法来探索所有可能的分割策略。

相关问题