leetcode
leetcode 1301 ~ 1350
恢复数组

恢复数组

难度:

标签:

题目描述

代码结果

运行时间: 460 ms, 内存: 20.3 MB


/*
 * 思路:
 * 使用 Stream 和 lambda 表达式来进行动态规划计算。
 */
import java.util.stream.IntStream;

public class Solution {
    public int numberOfArrays(String s, int k) {
        int n = s.length();
        int mod = 1000000007;
        int[] dp = new int[n + 1];
        dp[0] = 1;

        IntStream.rangeClosed(1, n).forEach(i -> {
            long[] num = {0};
            IntStream.iterate(i, j -> j - 1).limit(i).forEach(j -> {
                num[0] = num[0] + (s.charAt(j - 1) - '0') * (long)Math.pow(10, i - j);
                if (num[0] > k) return;
                if (s.charAt(j - 1) != '0') {
                    dp[i] = (dp[i] + dp[j - 1]) % mod;
                }
            });
        });

        return dp[n];
    }
}

解释

方法:

此题解采用动态规划的方法解决问题。定义 dp[i] 为字符串 s 的前 i 个字符可以形成的有效数组的方案数。初始化 dp[0] = 1,表示空字符串有一种划分方式(即没有数字)。遍历字符串 s 的每一个位置 i,如果当前位置字符为 '0' 或 dp[i] 为 0(表示无法从前面的字符转移至此),则直接跳过该位置。对于每个有效的起点 i,尝试将从位置 i 到 j 的子串转化为数字,并判断是否不超过 k。若不超过 k,则将 dp[i] 的方案数加到 dp[j+1] 上。这样,我们可以通过前面的位置来更新后面的位置的方案数。最终,dp[len(s)] 存储的是整个字符串 s 可以形成的有效数组的总方案数。

时间复杂度:

O(n^2)

空间复杂度:

O(n)

代码细节讲解

🦆
在动态规划数组 dp 中,dp[i] 表示的具体意义是什么?
在动态规划数组 dp 中,dp[i] 表示的是字符串 s 的前 i 个字符可以形成的有效数组的方案数。具体来说,dp[i] 记录了从字符串开始到第 i 个字符为止,所有可以从 s 中提取并转化为不超过 k 的整数的划分方式的总数。
🦆
该算法中为什么要在每次循环开始时检查 dp[i] 是否为0,它的含义是什么?
在该算法中,检查 dp[i] 是否为 0 的目的是为了确定从字符串的开始到第 i 个字符的位置能否形成有效的数字组合。如果 dp[i] 为 0,这意味着没有任何有效的方式可以将 s 的前 i 个字符划分成满足条件的整数序列,因此没有必要继续从这个位置尝试生成新的数字,可以直接跳过以提高效率。
🦆
为什么在算法中需要将数字字符转换成整数列表进行处理,直接使用字符串处理有什么不足?
将数字字符转换成整数列表进行处理主要是为了方便和效率。使用整数列表可以直接进行数字的相加和乘法操作,这在构建数字和进行比较时更为直观和快速。如果直接使用字符串,每次构造新数字或比较大小时都需要进行字符串到数字的转换,这会增加处理时间和复杂度。
🦆
在内层循环中,num 的构建方式可能会导致整数溢出吗?如果会,如何避免?
在内层循环中,由于 num 是通过连续地将数字追加到已有的数值后面(即 num = num * 10 + v[j])来构建的,这确实有可能导致整数溢出,特别是当 s 很长或数值 k 很大时。为了避免这种情况,算法中加入了一个检查点:一旦 num 超过了 k,就会立即终止内层循环。这样,不仅避免了不必要的计算,也防止了 num 增长到可能导致溢出的大小。

相关问题