Medium
题目描述
给你一个整数数组 nums,返回全为 0 的子数组的数目。
子数组是数组中一个连续非空的元素序列。
示例 1:
输入:nums = [1,3,0,0,2,0,0,4]
输出:6
解释:
子数组 [0] 出现了 4 次。
子数组 [0,0] 出现了 2 次。
没有长度超过 2 的全零子数组,所以我们返回 6。
示例 2:
输入:nums = [0,0,0,2,0,0]
输出:9
解释:
子数组 [0] 出现了 5 次。
子数组 [0,0] 出现了 3 次。
子数组 [0,0,0] 出现了 1 次。
没有长度超过 3 的全零子数组,所以我们返回 9。
示例 3:
输入:nums = [2,10,2019]
输出:0
解释:没有全零子数组,所以我们返回 0。
提示:
1 <= nums.length <= 10^5-10^9 <= nums[i] <= 10^9
解题思路
这道题的核心思想是:对于长度为 n 的连续零序列,它能构成 1 + 2 + … + n = n * (n + 1) / 2 个全零子数组。
解题思路
一次遍历法(推荐):遍历数组,维护当前连续零的长度。每遇到一个零,以该零结尾的全零子数组数量就是当前连续零的长度。
分段计算法:找出所有连续零的段,对每段计算子数组数量并累加。
以示例1为例:[1,3,0,0,2,0,0,4]
- 第一段连续零:
[0,0],长度2,贡献 2*(2+1)/2 = 3 个子数组 - 第二段连续零:
[0,0],长度2,贡献 2*(2+1)/2 = 3 个子数组 - 总计:3 + 3 = 6
推荐使用一次遍历法,它更直观且效率更高。关键在于理解:当我们遇到第 k 个连续的零时,以这个零结尾的全零子数组有 k 个(分别是长度为1, 2, …, k的子数组)。
代码实现
class Solution {
public:
long long zeroFilledSubarray(vector<int>& nums) {
long long result = 0;
long long consecutiveZeros = 0;
for (int num : nums) {
if (num == 0) {
consecutiveZeros++;
result += consecutiveZeros;
} else {
consecutiveZeros = 0;
}
}
return result;
}
};
class Solution:
def zeroFilledSubarray(self, nums: List[int]) -> int:
result = 0
consecutive_zeros = 0
for num in nums:
if num == 0:
consecutive_zeros += 1
result += consecutive_zeros
else:
consecutive_zeros = 0
return result
public class Solution {
public long ZeroFilledSubarray(int[] nums) {
long result = 0;
long consecutiveZeros = 0;
foreach (int num in nums) {
if (num == 0) {
consecutiveZeros++;
result += consecutiveZeros;
} else {
consecutiveZeros = 0;
}
}
return result;
}
}
var zeroFilledSubarray = function(nums) {
let count = 0;
let consecutive = 0;
for (let i = 0; i < nums.length; i++) {
if (nums[i] === 0) {
consecutive++;
count += consecutive;
} else {
consecutive = 0;
}
}
return count;
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n) | 只需要遍历数组一次 |
| 空间复杂度 | O(1) | 只使用了常数额外空间 |