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

标签: 数组 数学

难度: Medium

给你一个二元数组 nums

如果数组中的某个子数组 恰好 只存在 个值为 1 的元素,则认为该子数组是一个 好子数组

请你统计将数组 nums 划分成若干 好子数组 的方法数,并以整数形式返回。由于数字可能很大,返回其对 109 + 7 取余 之后的结果。

子数组是数组中的一个连续 非空 元素序列。

示例 1:

输入:nums = [0,1,0,0,1]
输出:3
解释:存在 3 种可以将 nums 划分成若干好子数组的方式:
- [0,1] [0,0,1]
- [0,1,0] [0,1]
- [0,1,0,0] [1]

示例 2:

输入:nums = [0,1,0]
输出:1
解释:存在 1 种可以将 nums 划分成若干好子数组的方式:
- [0,1,0]

提示:

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

Submission

运行时间: 144 ms

内存: 19.7 MB

class Solution:
    def numberOfGoodSubarraySplits(self, nums: List[int]) -> int:
        pre, res = -1, 1
        for idx, i in enumerate(nums):
            if i == 1:
                if pre != -1: res = (res * (idx-pre)) % 1_000_000_007
                pre = idx
        return res if pre > -1 else 0

Explain

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

时间复杂度: O(n)

空间复杂度: O(1)

class Solution:
    def numberOfGoodSubarraySplits(self, nums: List[int]) -> int:
        pre, res = -1, 1  # 初始化pre为-1表示之前没有遇到1,res为1表示初始乘积因子
        for idx, i in enumerate(nums):  # 遍历数组nums
            if i == 1:  # 检测到1
                if pre != -1: res = (res * (idx-pre)) % 1_000_000_007  # 如果不是第一个1,更新res
                pre = idx  # 更新1的位置
        return res if pre > -1 else 0  # 如果数组中有1,则返回res,否则返回0

Explore

动态规划的解法中,将上一个1的位置和当前1的位置之间的距离作为乘法因子来更新结果是为了计算从上一个1到当前1之间的所有可能的子数组数量。每多一个元素,就多一种组合方式,即从上一个1开始到当前位置的所有好子数组的组合方式。这种方法保证了每个子数组都至少包含一个1,并且能够有效地计算出所有可能的好子数组组合。

是的,如果整个数组中没有1,那么返回0确实意味着没有任何好子数组可以形成。在这个题目的上下文中,一个好子数组被定义为至少包含一个1的子数组。因此,如果数组中一个1都没有,就不可能形成满足条件的好子数组。

`res = (res * (idx-pre)) % 1_000_000_007`这一行代码主要是用来更新好子数组的数量。这里的`(idx-pre)`表示当前1的位置与上一个1的位置之间的距离,这个距离实际上代表了介于两个1之间的元素数量加1(即包含当前1的子数组的数量)。通过乘以这个距离,我们可以将之前所有可能的好子数组的数量乘以新的组合可能性。使用模1_000_000_007是为了防止计算过程中的溢出,保持结果的稳定性。

`pre`变量初始化为-1主要是为了处理数组中第一个1之前的情况。在没有遇到任何1之前,`pre`为-1可以帮助我们识别数组中的第一个1,并且在处理第一个1时不进行不必要的计算。这种初始化方式为处理数组开头的特殊情况提供了便利,并确保了算法从遇到第一个1开始正确计算好子数组的数量。