和相同的二元子数组
题目描述:
给你一个二元数组 nums ,和一个整数 goal ,请你统计并返回有多少个和为 goal 的 非空 子数组。
子数组 是数组的一段连续部分。
示例 :
输入: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]
提示:
- 1 <= nums.length <= 3 * 104
- nums[i] 不是 0 就是 1
- 0 <= goal <= nums.length
解法一 前缀和
- 连续子区间的加和其实就是两个前缀和的差值,可以简单记为sum(num[left: right]) = prefix_sum(right) - prefix_sum(left)。因此我们遍历右端点,也就是对于right,我们需要知道前方与prefix_sum(right) - goal相等的前缀和有多少个,所以需要在遍历的同时记录各个前缀和的次数。
- 在具体进行迭代时,需要注意到没有元素时的前缀和是 0,所以应该先将前缀和更新到字典中。
代码
class Solution:
def numSubarraysWithSum(self, nums: List[int], goal: int) -> int:
if goal > len(nums):
return 0
sum2cnt = {}
prefix_sum, res = 0, 0
for num in nums:
sum2cnt[prefix_sum] = sum2cnt.get(prefix_sum, 0) + 1
prefix_sum += num
res += sum2cnt.get(prefix_sum - goal, 0)
return res
测试结果
执行用时:228 ms, 在所有 Python3 提交中击败了 53.30% 的用户
内存消耗:18.3 MB, 在所有 Python3 提交中击败了 38.07% 的用户
解法二 滑动窗口
看到本题很容易想到滑动窗口的解法,但容易发现,对于某个右端点right,符合要求的左端点可能不止一个,所以左端点需要用两个变量来描述,[left1, left2)中的整数下标left均满足:sum(num[left: right + 1]) == goal。如此,每次在移动右端点的时候,也对左端点的区间进行更新,符合条件的窗口个数即为left2 - left1。
代码
class Solution:
def numSubarraysWithSum(self, nums: List[int], goal: int) -> int:
if goal > len(nums):
return 0
res = 0
left1, sum1, left2, sum2 = 0, 0, 0, 0
for right, item in enumerate(nums):
sum1 += item
while left1 <= right and sum1 > goal:
sum1 -= nums[left1]
left1 += 1
sum2 += item
while left2 <= right and sum2 >= goal:
sum2 -= nums[left2]
left2 += 1
res += left2 - left1
return res
测试结果
执行用时:252 ms, 在所有 Python3 提交中击败了 53.30% 的用户
内存消耗:15.5 MB, 在所有 Python3 提交中击败了 92.39% 的用户
说明
算法题来源:力扣(LeetCode)