930. Binary Subarrays with Sum - Explanation
Description
You are given a binary array nums and an integer goal, return the number of non-empty subarrays with a sum goal.
A subarray is a contiguous part of the array.
Example 1:
Input: nums = [1,0,1,0,1], goal = 2
Output: 4Explanation: The 4 subarrays are shown below:
[1,0,1,0,1]
[1,0,1,0,1]
[1,0,1,0,1]
[1,0,1,0,1]
Example 2:
Input: nums = [0,0,0,0,0], goal = 0
Output: 15Constraints:
1 <= nums.length <= 30,000nums[i]is either0or1.0 <= goal <= nums.length
Topics
Prerequisites
Before attempting this problem, you should be comfortable with:
- Prefix Sum - Enables O(1) subarray sum queries after O(n) preprocessing
- Hash Map - Used to count occurrences of prefix sums for efficient lookup
- Sliding Window / Two Pointers - Alternative approach using "at most K" technique
1. Brute Force
Intuition
The most straightforward approach is to check every possible subarray. For each starting index, we extend the subarray one element at a time, keeping track of the running sum. Whenever the sum equals the goal, we count it. Since the array contains only 0s and 1s, the sum can only increase as we extend the subarray.
Algorithm
- Initialize a result counter to 0.
- For each starting index
ifrom0ton-1, initialize a running sum to0. - For each ending index
jfromiton-1, addnums[j]to the running sum. - If the running sum equals the goal, increment the result counter.
- Return the total count.
Time & Space Complexity
- Time complexity:
- Space complexity:
2. Prefix Sum + Hash Map
Intuition
For a subarray from index i+1 to j to have sum equal to goal, we need prefixSum[j] - prefixSum[i] = goal, which means prefixSum[i] = prefixSum[j] - goal. As we iterate through the array computing prefix sums, we can use a hash map to count how many times each prefix sum has occurred. For each position, we check how many previous positions had a prefix sum that would give us our target subarray sum.
Algorithm
- Initialize a hash map with
{0: 1}to handle subarrays starting from index0. - Initialize prefix sum and result counter to
0. - For each element, add it to the prefix sum.
- Look up
prefixSum - goalin the hash map and add its count to the result. - Increment the count of the current prefix sum in the hash map.
- Return the total count.
class Solution:
def numSubarraysWithSum(self, nums: List[int], goal: int) -> int:
prefixSum = 0
count = { 0 : 1 } # prefixSum -> count
res = 0
for num in nums:
prefixSum += num
res += count.get(prefixSum - goal, 0)
count[prefixSum] = count.get(prefixSum, 0) + 1
return resTime & Space Complexity
- Time complexity:
- Space complexity:
3. Prefix Sum + Array
Intuition
Since the array contains only 0s and 1s, the prefix sum at any position is at most n (the array length). This allows us to use an array instead of a hash map for counting prefix sums. Array access is faster than hash map operations, making this approach more efficient in practice. The logic remains the same as the hash map approach.
Algorithm
- Create an array of size
n+1to count prefix sums, initialized to0. - Set
count[0] = 1to handle subarrays starting from index0. - Initialize prefix sum and result counter to
0. - For each element, add it to the prefix sum.
- If
prefixSum >= goal, addcount[prefixSum - goal]to the result. - Increment
count[prefixSum]. - Return the total count.
class Solution:
def numSubarraysWithSum(self, nums: List[int], goal: int) -> int:
n = len(nums)
count = [0] * (n + 1)
count[0] = 1
prefixSum, res = 0, 0
for num in nums:
prefixSum += num
if prefixSum >= goal:
res += count[prefixSum - goal]
count[prefixSum] += 1
return resTime & Space Complexity
- Time complexity:
- Space complexity:
4. Sliding Window
Intuition
Counting subarrays with exactly goal sum is tricky with a sliding window because shrinking the window might skip valid subarrays. However, counting subarrays with sum at most goal is straightforward. We can use the identity: count(exactly goal) = count(at most goal) - count(at most goal-1). For each right endpoint, we shrink the left side until the sum is at most the target, and all subarrays ending at right with starting points from left to right are valid.
Algorithm
- Define a helper function that counts subarrays with sum at most
x. - In the helper, use two pointers (
leftandright) with a running sum. - For each
right, addnums[right]to the sum. Whilesum > x, shrink from the left. - Add
(right - left + 1)to the count, representing all valid subarrays ending atright. - Return
helper(goal) - helper(goal - 1)as the final answer.
class Solution:
def numSubarraysWithSum(self, nums: List[int], goal: int) -> int:
def helper(x):
if x < 0:
return 0
res = l = cur = 0
for r in range(len(nums)):
cur += nums[r]
while cur > x:
cur -= nums[l]
l += 1
res += (r - l + 1)
return res
return helper(goal) - helper(goal - 1)Time & Space Complexity
- Time complexity:
- Space complexity:
Common Pitfalls
Forgetting to Initialize Prefix Sum Count with Zero
The hash map must start with {0: 1} to count subarrays that start from index 0. Without this, subarrays where the prefix sum exactly equals the goal are missed.
# Wrong: count = {}
# Correct: count = {0: 1}Not Handling goal = 0 in Sliding Window
When using the sliding window approach, helper(goal - 1) becomes helper(-1). Without the early return for negative values, the window logic breaks and may produce incorrect results or infinite loops.
# Wrong: missing the x < 0 check
# Correct: if x < 0: return 0Breaking Early When Sum Exceeds Goal
In the brute force approach, some incorrectly add a break when curSum > goal. This is wrong because the array contains only 0s and 1s, and adding more 0s keeps the sum unchanged, potentially finding more valid subarrays.
Sign in to join the discussion