560. Subarray Sum Equals K
Problem Description
You are given an array of integers nums
and an integer k
. Your task is to find the total number of subarrays whose sum equals to k
.
A subarray is a contiguous non-empty sequence of elements within an array. This means the elements must be consecutive in the original array.
For example:
- If
nums = [1, 2, 3]
andk = 3
, the subarrays with sum equal to 3 are[1, 2]
and[3]
, so the answer would be 2. - The subarray must contain at least one element (non-empty).
- The elements in the subarray must appear consecutively in the original array.
The solution uses a hash table combined with prefix sum technique. The key insight is that if we have a prefix sum s
at current position and we previously had a prefix sum of s - k
at some earlier position, then the subarray between those two positions has a sum of exactly k
.
The algorithm works as follows:
- Initialize a counter
cnt
with{0: 1}
to handle cases where the prefix sum itself equalsk
- Iterate through each number in the array, maintaining a running sum
s
- For each position, check how many times we've seen the prefix sum
s - k
before (this tells us how many subarrays ending at current position have sumk
) - Add the current prefix sum
s
to our counter for future use - Return the total count of valid subarrays
Intuition
The brute force approach would be to check every possible subarray and count those with sum equal to k
. This would require O(n²) or O(n³) time complexity, which is inefficient for large arrays.
The key observation is that we can use prefix sums to optimize this. A prefix sum at index i
represents the sum of all elements from index 0 to index i
. If we know the prefix sum at two different positions, we can calculate the sum of the subarray between them.
Here's the crucial insight: If the current prefix sum is s
and we want a subarray with sum k
, we need to find if there was a previous prefix sum equal to s - k
. Why? Because if there was a prefix sum of s - k
at some earlier position j
, and the current prefix sum at position i
is s
, then the subarray from j+1
to i
has sum: s - (s - k) = k
.
For example, if nums = [1, 2, 3, 4]
and k = 5
:
- At index 2, prefix sum = 6 (1+2+3)
- We look for prefix sum = 6 - 5 = 1
- Prefix sum 1 exists at index 0
- So subarray from index 1 to 2 is [2, 3] with sum 5
To efficiently track how many times each prefix sum has occurred, we use a hash table. This allows us to instantly check if s - k
exists and how many times it has appeared (each occurrence represents a different valid subarray ending at the current position).
We initialize the hash table with {0: 1}
because a prefix sum of 0 exists before we start (representing an empty prefix). This handles cases where the prefix sum from the start equals k
.
Learn more about Prefix Sum patterns.
Solution Approach
The solution uses a hash table combined with prefix sum technique to solve the problem efficiently in O(n) time.
Data Structure Used:
- Hash table
cnt
(Counter in Python) to store the frequency of each prefix sum value
Algorithm Steps:
-
Initialize the hash table: Create
cnt
with initial value{0: 1}
. This represents that before processing any elements, we have seen prefix sum 0 exactly once. This is crucial for handling cases where a prefix sum from the beginning equalsk
. -
Initialize variables:
ans = 0
to store the count of valid subarrayss = 0
to maintain the running prefix sum
-
Iterate through the array: For each element
x
innums
:a. Update prefix sum: Add current element to running sum:
s += x
b. Count valid subarrays: Check if
s - k
exists in our hash table. If it does, addcnt[s - k]
to our answer. This represents all subarrays ending at the current position with sum equal tok
.c. Update hash table: Increment the count of current prefix sum:
cnt[s] += 1
. This records that we've seen this prefix sum value one more time, which can be used for future positions. -
Return the result: After processing all elements, return
ans
which contains the total count of subarrays with sum equal tok
.
Why this works:
- When we're at position
i
with prefix sums
, and we find thats - k
appearedm
times before, it means there arem
different starting positions that create subarrays ending at positioni
with sum exactlyk
. - By maintaining the frequency of all prefix sums seen so far, we can instantly determine how many valid subarrays end at each position.
Time Complexity: O(n) - single pass through the array Space Complexity: O(n) - hash table can store at most n+1 different prefix sums
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the algorithm with nums = [1, 2, 1, 2]
and k = 3
.
Initial Setup:
cnt = {0: 1}
(we've seen prefix sum 0 once)ans = 0
(no subarrays found yet)s = 0
(current prefix sum)
Step 1: Process nums[0] = 1
- Update prefix sum:
s = 0 + 1 = 1
- Check for
s - k = 1 - 3 = -2
in cnt: not found, so no subarrays ending here - Update cnt:
cnt = {0: 1, 1: 1}
ans = 0
Step 2: Process nums[1] = 2
- Update prefix sum:
s = 1 + 2 = 3
- Check for
s - k = 3 - 3 = 0
in cnt: found with count 1- This means subarray [1, 2] from start has sum 3
- Add to answer:
ans = 0 + 1 = 1
- Update cnt:
cnt = {0: 1, 1: 1, 3: 1}
Step 3: Process nums[2] = 1
- Update prefix sum:
s = 3 + 1 = 4
- Check for
s - k = 4 - 3 = 1
in cnt: found with count 1- This means subarray [2, 1] (from index 1 to 2) has sum 3
- Add to answer:
ans = 1 + 1 = 2
- Update cnt:
cnt = {0: 1, 1: 1, 3: 1, 4: 1}
Step 4: Process nums[3] = 2
- Update prefix sum:
s = 4 + 2 = 6
- Check for
s - k = 6 - 3 = 3
in cnt: found with count 1- This means subarray [1, 2] (from index 2 to 3) has sum 3
- Add to answer:
ans = 2 + 1 = 3
- Update cnt:
cnt = {0: 1, 1: 1, 3: 1, 4: 1, 6: 1}
Result: The algorithm returns ans = 3
The three subarrays with sum 3 are:
- [1, 2] at indices 0-1
- [2, 1] at indices 1-2
- [1, 2] at indices 2-3
Solution Implementation
1from typing import List
2from collections import Counter
3
4class Solution:
5 def subarraySum(self, nums: List[int], k: int) -> int:
6 # Initialize hash map to store prefix sum frequencies
7 # Start with {0: 1} to handle subarrays starting from index 0
8 prefix_sum_count = Counter({0: 1})
9
10 # Initialize result counter and running prefix sum
11 result = 0
12 current_sum = 0
13
14 # Iterate through each number in the array
15 for num in nums:
16 # Update the running prefix sum
17 current_sum += num
18
19 # Check if (current_sum - k) exists in our hash map
20 # If it exists, it means there are subarrays ending at current index
21 # whose sum equals k
22 result += prefix_sum_count[current_sum - k]
23
24 # Add current prefix sum to the hash map for future iterations
25 prefix_sum_count[current_sum] += 1
26
27 return result
28
1class Solution {
2 public int subarraySum(int[] nums, int k) {
3 // HashMap to store cumulative sum frequencies
4 // Key: cumulative sum, Value: frequency of that sum
5 Map<Integer, Integer> prefixSumCount = new HashMap<>();
6
7 // Initialize with sum 0 having frequency 1
8 // This handles the case when a subarray from index 0 equals k
9 prefixSumCount.put(0, 1);
10
11 int count = 0; // Count of subarrays with sum equal to k
12 int currentSum = 0; // Running cumulative sum
13
14 // Iterate through each element in the array
15 for (int num : nums) {
16 // Update the cumulative sum
17 currentSum += num;
18
19 // Check if (currentSum - k) exists in the map
20 // If it exists, it means there are subarrays ending at current index with sum = k
21 // The frequency of (currentSum - k) tells us how many such subarrays exist
22 count += prefixSumCount.getOrDefault(currentSum - k, 0);
23
24 // Update the frequency of current cumulative sum in the map
25 // If currentSum already exists, increment its frequency by 1
26 // Otherwise, add it with frequency 1
27 prefixSumCount.merge(currentSum, 1, Integer::sum);
28 }
29
30 return count;
31 }
32}
33
1class Solution {
2public:
3 int subarraySum(vector<int>& nums, int k) {
4 // Hash map to store the frequency of prefix sums
5 // Initialize with {0: 1} to handle subarrays starting from index 0
6 unordered_map<int, int> prefixSumCount{{0, 1}};
7
8 int result = 0; // Count of subarrays with sum equal to k
9 int currentSum = 0; // Running prefix sum
10
11 // Iterate through each element in the array
12 for (int num : nums) {
13 // Update the current prefix sum
14 currentSum += num;
15
16 // Check if there exists a prefix sum such that
17 // currentSum - prefixSum = k (i.e., prefixSum = currentSum - k)
18 // If yes, add its frequency to the result
19 result += prefixSumCount[currentSum - k];
20
21 // Increment the frequency of the current prefix sum
22 ++prefixSumCount[currentSum];
23 }
24
25 return result;
26 }
27};
28
1/**
2 * Finds the total number of continuous subarrays whose sum equals k
3 * Uses prefix sum and hash map technique for O(n) time complexity
4 *
5 * @param nums - The input array of numbers
6 * @param k - The target sum to find
7 * @returns The count of subarrays with sum equal to k
8 */
9function subarraySum(nums: number[], k: number): number {
10 // Map to store frequency of prefix sums
11 // Key: prefix sum value, Value: count of occurrences
12 const prefixSumCount: Map<number, number> = new Map<number, number>();
13
14 // Initialize with sum 0 having count 1 (empty prefix)
15 prefixSumCount.set(0, 1);
16
17 let result: number = 0; // Total count of valid subarrays
18 let currentSum: number = 0; // Running prefix sum
19
20 // Iterate through each element in the array
21 for (const num of nums) {
22 // Update the running prefix sum
23 currentSum += num;
24
25 // Check if (currentSum - k) exists in map
26 // If it exists, it means we found subarray(s) with sum = k
27 const targetSum: number = currentSum - k;
28 result += prefixSumCount.get(targetSum) || 0;
29
30 // Update the frequency of current prefix sum in the map
31 prefixSumCount.set(currentSum, (prefixSumCount.get(currentSum) || 0) + 1);
32 }
33
34 return result;
35}
36
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the array nums
. The algorithm iterates through the array exactly once with a single for loop. Within each iteration, all operations are constant time: adding to the running sum s
, accessing and updating the Counter dictionary cnt[s - k]
and cnt[s]
, and incrementing ans
. Dictionary operations (lookup and insertion) in Python have an average time complexity of O(1)
.
The space complexity is O(n)
, where n
is the length of the array nums
. In the worst case, when all prefix sums are unique, the Counter dictionary cnt
will store up to n + 1
distinct prefix sum values (including the initial 0
). Each entry in the dictionary requires O(1)
space, resulting in a total space complexity of O(n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Forgetting to Initialize the Hash Map with {0: 1}
The Problem:
A common mistake is initializing an empty hash map instead of starting with {0: 1}
. This causes the algorithm to miss subarrays that start from index 0 and have sum equal to k
.
Example of Incorrect Code:
def subarraySum(self, nums: List[int], k: int) -> int:
prefix_sum_count = Counter() # Wrong! Missing {0: 1}
result = 0
current_sum = 0
for num in nums:
current_sum += num
result += prefix_sum_count[current_sum - k]
prefix_sum_count[current_sum] += 1
return result
Why This Fails:
- For
nums = [3, 2]
andk = 5
, the correct answer is 1 (subarray[3, 2]
) - When processing index 1,
current_sum = 5
and we check forprefix_sum_count[5 - 5] = prefix_sum_count[0]
- Without the initial
{0: 1}
, this returns 0 instead of 1, missing the valid subarray
Solution:
Always initialize with prefix_sum_count = Counter({0: 1})
to handle subarrays starting from the beginning.
Pitfall 2: Updating Hash Map Before Checking for Valid Subarrays
The Problem: Another mistake is updating the hash map with the current prefix sum before checking for valid subarrays. This can lead to counting invalid subarrays (specifically, empty subarrays).
Example of Incorrect Code:
def subarraySum(self, nums: List[int], k: int) -> int:
prefix_sum_count = Counter({0: 1})
result = 0
current_sum = 0
for num in nums:
current_sum += num
prefix_sum_count[current_sum] += 1 # Wrong order!
result += prefix_sum_count[current_sum - k]
return result
Why This Fails:
- When
k = 0
, this approach incorrectly counts empty subarrays - For
nums = [0]
andk = 0
, after processing the element:current_sum = 0
- We increment
prefix_sum_count[0]
to 2 - Then we check
prefix_sum_count[0 - 0] = 2
, giving us 2 instead of 1
- The extra count comes from incorrectly considering an empty subarray
Solution: Always check for valid subarrays first, then update the hash map:
result += prefix_sum_count[current_sum - k] # Check first prefix_sum_count[current_sum] += 1 # Then update
Pitfall 3: Using Regular Dictionary Instead of Counter
The Problem: Using a regular dictionary and not handling missing keys properly can cause KeyError exceptions.
Example of Incorrect Code:
def subarraySum(self, nums: List[int], k: int) -> int:
prefix_sum_count = {0: 1} # Regular dict
result = 0
current_sum = 0
for num in nums:
current_sum += num
result += prefix_sum_count[current_sum - k] # KeyError if key doesn't exist!
if current_sum in prefix_sum_count:
prefix_sum_count[current_sum] += 1
else:
prefix_sum_count[current_sum] = 1
return result
Solution:
Either use Counter
which returns 0 for missing keys, or use dict.get()
with a default value:
# Option 1: Use Counter (recommended) prefix_sum_count = Counter({0: 1}) result += prefix_sum_count[current_sum - k] # Returns 0 if key doesn't exist # Option 2: Use dict.get() prefix_sum_count = {0: 1} result += prefix_sum_count.get(current_sum - k, 0)
Given a sorted array of integers and an integer called target, find the element that
equals to the target and return its index. Select the correct code that fills the
___
in the given code snippet.
1def binary_search(arr, target):
2 left, right = 0, len(arr) - 1
3 while left ___ right:
4 mid = (left + right) // 2
5 if arr[mid] == target:
6 return mid
7 if arr[mid] < target:
8 ___ = mid + 1
9 else:
10 ___ = mid - 1
11 return -1
12
1public static int binarySearch(int[] arr, int target) {
2 int left = 0;
3 int right = arr.length - 1;
4
5 while (left ___ right) {
6 int mid = left + (right - left) / 2;
7 if (arr[mid] == target) return mid;
8 if (arr[mid] < target) {
9 ___ = mid + 1;
10 } else {
11 ___ = mid - 1;
12 }
13 }
14 return -1;
15}
16
1function binarySearch(arr, target) {
2 let left = 0;
3 let right = arr.length - 1;
4
5 while (left ___ right) {
6 let mid = left + Math.trunc((right - left) / 2);
7 if (arr[mid] == target) return mid;
8 if (arr[mid] < target) {
9 ___ = mid + 1;
10 } else {
11 ___ = mid - 1;
12 }
13 }
14 return -1;
15}
16
Recommended Readings
Prefix Sum The prefix sum is an incredibly powerful and straightforward technique Its primary goal is to allow for constant time range sum queries on an array What is Prefix Sum The prefix sum of an array at index i is the sum of all numbers from index 0 to i By
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!