leetcode
leetcode 2351 ~ 2400
将数组划分成若干好子数组的方式

将数组划分成若干好子数组的方式

难度:

标签:

题目描述

You are given a binary array nums.

A subarray of an array is good if it contains exactly one element with the value 1.

Return an integer denoting the number of ways to split the array nums into good subarrays. As the number may be too large, return it modulo 109 + 7.

A subarray is a contiguous non-empty sequence of elements within an array.

 

Example 1:

Input: nums = [0,1,0,0,1]
Output: 3
Explanation: There are 3 ways to split nums into good subarrays:
- [0,1] [0,0,1]
- [0,1,0] [0,1]
- [0,1,0,0] [1]

Example 2:

Input: nums = [0,1,0]
Output: 1
Explanation: There is 1 way to split nums into good subarrays:
- [0,1,0]

 

Constraints:

  • 1 <= nums.length <= 105
  • 0 <= nums[i] <= 1

代码结果

运行时间: 144 ms, 内存: 19.7 MB


/*
 * 给定一个二元数组 nums。
 * 如果某个子数组恰好只存在一个值为 1 的元素,则认为该子数组是一个好子数组。
 * 要统计将数组 nums 划分成若干好子数组的方法数,并返回其对 10^9 + 7 取余之后的结果。
 * 
 * 思路:
 * 1. 使用 Java Stream 记录所有 1 的位置。
 * 2. 计算每两个相邻 1 之间的距离,然后可以选择插入这些位置的好子数组数目。
 * 3. 最后将所有的可能性相乘,即为结果。
 */

import java.util.*;
import java.util.stream.*;

public class GoodSubarraysStream {
    public int numGoodSubarrays(int[] nums) {
        final int MOD = 1000000007;
        List<Integer> onesPositions = IntStream.range(0, nums.length)
                                               .filter(i -> nums[i] == 1)
                                               .boxed()
                                               .collect(Collectors.toList());
        if (onesPositions.size() == 0) return 0;
        long result = IntStream.range(1, onesPositions.size())
                               .mapToLong(i -> onesPositions.get(i) - onesPositions.get(i - 1))
                               .reduce(1, (a, b) -> (a * b) % MOD);
        return (int) result;
    }

    public static void main(String[] args) {
        GoodSubarraysStream gs = new GoodSubarraysStream();
        int[] nums1 = {0, 1, 0, 0, 1};
        System.out.println(gs.numGoodSubarrays(nums1)); // 输出: 3

        int[] nums2 = {0, 1, 0};
        System.out.println(gs.numGoodSubarrays(nums2)); // 输出: 1
    }
}

解释

方法:

此题解的核心思想是使用动态规划的思路来统计好子数组的分割方法。首先,定义两个变量pre用于记录上一个数字1的位置(初始化为-1),res用于统计分割方法的总数(初始化为1)。遍历数组,每当遇到数字1时,若之前已遇到过数字1(即pre不为-1),则计算当前1的位置与上一个1的位置之间的元素个数,并用这个距离乘以之前的统计结果更新res,这样做是因为新的1可以与之前的所有好子数组组合形成新的好子数组。最后,如果整个数组中至少有一个1,返回res,否则返回0(没有好子数组的情况)。

时间复杂度:

O(n)

空间复杂度:

O(1)

代码细节讲解

🦆
在动态规划的解法中,为什么选择将上一个1的位置和当前1的位置之间的距离作为乘法因子来更新结果?
动态规划的解法中,将上一个1的位置和当前1的位置之间的距离作为乘法因子来更新结果是为了计算从上一个1到当前1之间的所有可能的子数组数量。每多一个元素,就多一种组合方式,即从上一个1开始到当前位置的所有好子数组的组合方式。这种方法保证了每个子数组都至少包含一个1,并且能够有效地计算出所有可能的好子数组组合。
🦆
题解中提到如果整个数组中没有1,则返回0。这种情况是否意味着没有任何好子数组可以形成,即使数组中全部是0?
是的,如果整个数组中没有1,那么返回0确实意味着没有任何好子数组可以形成。在这个题目的上下文中,一个好子数组被定义为至少包含一个1的子数组。因此,如果数组中一个1都没有,就不可能形成满足条件的好子数组。
🦆
如何理解题解中提到的`res = (res * (idx-pre)) % 1_000_000_007`这一行代码?具体来说,这里的`(idx-pre)`代表什么,为什么需要这样更新res?
`res = (res * (idx-pre)) % 1_000_000_007`这一行代码主要是用来更新好子数组的数量。这里的`(idx-pre)`表示当前1的位置与上一个1的位置之间的距离,这个距离实际上代表了介于两个1之间的元素数量加1(即包含当前1的子数组的数量)。通过乘以这个距离,我们可以将之前所有可能的好子数组的数量乘以新的组合可能性。使用模1_000_000_007是为了防止计算过程中的溢出,保持结果的稳定性。
🦆
在题解的实现中,`pre`变量初始化为-1,这种初始化方式有什么特殊的意义或好处?
`pre`变量初始化为-1主要是为了处理数组中第一个1之前的情况。在没有遇到任何1之前,`pre`为-1可以帮助我们识别数组中的第一个1,并且在处理第一个1时不进行不必要的计算。这种初始化方式为处理数组开头的特殊情况提供了便利,并确保了算法从遇到第一个1开始正确计算好子数组的数量。

相关问题