题目描述
给你一个二元数组 nums ,和一个整数 goal ,请你统计并返回有多少个和为 goal 的 非空 子数组。
子数组 是数组的一段连续部分。
示例 1:
输入:nums = [1,0,1,0,1], goal = 2
输出:4
解释:
如下面黑体所示,有 4 个满足题目要求的子数组:
[1,0,1,0,1]
[1,0,1,0,1]
[1,0,1,0,1]
[1,0,1,0,1]示例 2:
输入:nums = [0,0,0,0,0], goal = 0
输出:15
提示:
1 <= nums.length <= 3 * 104
nums[i] 不是 0 就是 1
0 <= goal <= nums.length
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/binary-subarrays-with-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路
思路一:哈希表+前缀和
利用哈希表存储<数组前缀和, 出现的频次>,如此一来当前缀和preSum已经大于等于goal的时候,计数需要加上hashmap.getOrDefault(preSum-goal, 0),(preSum-goal代表子数组的和)且如果preSum刚好等于goal,计数额外+1
思路二:滑窗
需要用到两个左指针left1和left2,一个右指针right,以及两个求和变量sum1和sum2。l e f t 1 ≤ l e f t 2 ≤ r i g h t left1 \leq left2 \leq rightleft1≤left2≤right
外层循环每次移动一次右指针,内层循环分为对left1的循环和对left2的循环
- left1的循环为:left1每次右移一个,直到[left1, right]子数组的和小于goal
- left2的循环为:left2每次右移一个,直到[left1, right]子数组的和小于等于goal
如此一来,left2-left1即为当前右指针right下,子数组和等于goal的个数,这个“个数”随着右指针的不断右移而不断累加
代码详解
思路一:哈希表+前缀和
class Solution {
public int numSubarraysWithSum(int[] nums, int goal) {
int preSum = 0; // 前缀和
int count = 0; // 计数器
Map<Integer, Integer> hm = new HashMap<>(); // 保存<前缀和, 出现的频次>
for(int num : nums) {
preSum += num;
if(preSum >= goal) { // 在前缀和大于等于goal的情况下
if(preSum == goal) { // 如果等于则计数器额外+1
++count;
}
count += hm.getOrDefault(preSum - goal, 0); // 前缀和之差即为区间和
}
hm.put(preSum, hm.getOrDefault(preSum, 0) + 1); // 更新哈希表
}
return count;
}
}
思路二:滑窗
class Solution {
public int numSubarraysWithSum(int[] nums, int goal) {
int count = 0; // 计数器
int left1 = 0; // 左指针1,小于等于左指针2,小于等于右指针
int left2 = 0; // 左指针2,小于等于右指针
int right = 0; // 右指针
int sum1 = 0; // 区间和1,对应[左指针1, 右指针]
int sum2 = 0; // 区间和2,对应[左指针2, 右指针]
for(int num : nums) {
sum1 += num;
while(left1 <= right && sum1 > goal) { // 注意条件是sum1大于goal
sum1 -= nums[left1];
++left1; // 1号滑窗左端点右移,滑窗减小
}
sum2 += num;
while(left2 <= right && sum2 >= goal) { // 注意条件是sum2大于等于goal
sum2 -= nums[left2];
++left2; // 2号滑窗左端点右移,滑窗减小
}
// 1号滑窗与2号滑窗相差的地方就只在等于goal这种情况,因此left2-left1即为当前right指针下,子数组和等于goal的个数
count += left2 - left1;
++right; // 滑窗右端点右移,滑窗扩大
}
return count;
}
}
注意点
- 针对子区间的问题常想到滑窗,而对于数字数组的子区间问题还可以考虑前缀和