325. Maximum Size Subarray Sum Equals k 🔒
Problem Description
You are given an integer array nums
and an integer k
. Your task is to find the maximum length of a contiguous subarray whose elements sum to exactly k
. If no such subarray exists, return 0
.
A subarray is a contiguous sequence of elements within an array. For example, in the array [1, 2, 3, 4]
, some subarrays include [1, 2]
, [2, 3, 4]
, and [3]
, but [1, 3]
is not a subarray because the elements are not contiguous.
The problem asks you to:
- Find all possible contiguous subarrays of
nums
- Check which ones have a sum equal to
k
- Return the length of the longest such subarray
- If no subarray sums to
k
, return0
For example:
- If
nums = [1, -1, 5, -2, 3]
andk = 3
, the subarray[1, -1, 5, -2]
sums to3
and has length4
, which would be the answer. - If
nums = [2, 3, 4]
andk = 10
, no subarray sums to10
, so the answer would be0
.
Intuition
The naive approach would be to check all possible subarrays and find the longest one with sum k
. However, this would require O(n²)
or O(n³)
time complexity, which is inefficient for large arrays.
The key insight is to use prefix sums. If we know the cumulative sum up to each position, we can calculate any subarray sum quickly. Specifically, if we have the prefix sum at position i
as s
, and we want a subarray ending at position i
with sum k
, then we need to find if there exists a position j
where the prefix sum was s - k
.
Why does this work? Because the subarray from position j+1
to i
would have sum = (prefix sum at i) - (prefix sum at j)
= s - (s - k)
= k
.
To implement this efficiently, we can use a hash table to store the first occurrence of each prefix sum value. As we iterate through the array:
- We calculate the running prefix sum
s
- We check if
s - k
exists in our hash table - If it does, we've found a subarray with sum
k
, and we can calculate its length - We store the current prefix sum
s
with its index if it hasn't been seen before
The reason we only store the first occurrence of each prefix sum is crucial: if the same prefix sum appears multiple times, using the earliest occurrence gives us the longest possible subarray ending at the current position.
We initialize the hash table with {0: -1}
to handle the case where the subarray starts from index 0
. This way, if the prefix sum equals k
exactly, we can correctly calculate the length as i - (-1) = i + 1
.
Solution Approach
We implement the solution using a hash table and prefix sum technique:
-
Initialize the hash table: Create a dictionary
d
with initial value{0: -1}
. This handles the case where a subarray starting from index 0 has sum equal tok
. -
Initialize tracking variables:
ans = 0
: Tracks the maximum length found so fars = 0
: Maintains the running prefix sum
-
Iterate through the array: For each element at index
i
with valuex
:a. Update prefix sum: Add the current element to the running sum:
s += x
b. Check for valid subarray: If
s - k
exists in the hash tabled
, it means we've found a subarray with sumk
:- The subarray spans from index
d[s - k] + 1
to indexi
- Its length is
i - d[s - k]
- Update
ans
with the maximum of currentans
and this new length:ans = max(ans, i - d[s - k])
c. Store prefix sum: If the current prefix sum
s
hasn't been seen before (not ind
), store it with its index:d[s] = i
- We only store the first occurrence to maximize potential subarray lengths
- If
s
already exists ind
, we don't update it because keeping the earlier index gives us longer subarrays
- The subarray spans from index
-
Return the result: After processing all elements, return
ans
which contains the maximum length found.
The algorithm runs in O(n)
time complexity with a single pass through the array, and uses O(n)
space complexity for the hash table in the worst case where all prefix sums are unique.
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 solution with nums = [1, -1, 5, -2, 3]
and k = 3
.
We'll track the prefix sum s
, our hash table d
, and the maximum length ans
.
Initial state:
d = {0: -1}
(handles subarrays starting from index 0)s = 0
(running prefix sum)ans = 0
(maximum length found)
Step 1: Process nums[0] = 1 (i = 0)
- Update prefix sum:
s = 0 + 1 = 1
- Check if
s - k = 1 - 3 = -2
exists ind
: No - Store prefix sum:
d[1] = 0
, sod = {0: -1, 1: 0}
- Current
ans = 0
Step 2: Process nums[1] = -1 (i = 1)
- Update prefix sum:
s = 1 + (-1) = 0
- Check if
s - k = 0 - 3 = -3
exists ind
: No - Check if
s = 0
exists ind
: Yes! It's already there (from initialization)- We don't update
d[0]
because we keep the first occurrence
- We don't update
- So
d = {0: -1, 1: 0}
- Current
ans = 0
Step 3: Process nums[2] = 5 (i = 2)
- Update prefix sum:
s = 0 + 5 = 5
- Check if
s - k = 5 - 3 = 2
exists ind
: No - Store prefix sum:
d[5] = 2
, sod = {0: -1, 1: 0, 5: 2}
- Current
ans = 0
Step 4: Process nums[3] = -2 (i = 3)
- Update prefix sum:
s = 5 + (-2) = 3
- Check if
s - k = 3 - 3 = 0
exists ind
: Yes!d[0] = -1
- Found a subarray from index
(-1) + 1 = 0
to index3
- Length =
3 - (-1) = 4
- Update
ans = max(0, 4) = 4
- Found a subarray from index
- Store prefix sum:
d[3] = 3
, sod = {0: -1, 1: 0, 5: 2, 3: 3}
- Current
ans = 4
Step 5: Process nums[4] = 3 (i = 4)
- Update prefix sum:
s = 3 + 3 = 6
- Check if
s - k = 6 - 3 = 3
exists ind
: Yes!d[3] = 3
- Found a subarray from index
3 + 1 = 4
to index4
- Length =
4 - 3 = 1
- Update
ans = max(4, 1) = 4
- Found a subarray from index
- Store prefix sum:
d[6] = 4
, sod = {0: -1, 1: 0, 5: 2, 3: 3, 6: 4}
- Current
ans = 4
Result: The maximum length is 4
, corresponding to the subarray [1, -1, 5, -2]
which sums to 3
.
The key insight: When we reached index 3 with prefix sum 3, we found that prefix sum 0 existed at index -1. This means the subarray from index 0 to 3 has sum = 3 - 0 = 3, giving us our answer.
Solution Implementation
1class Solution:
2 def maxSubArrayLen(self, nums: List[int], k: int) -> int:
3 # HashMap to store (cumulative_sum: first_occurrence_index)
4 # Initialize with sum 0 at index -1 (before array starts)
5 sum_to_index = {0: -1}
6
7 max_length = 0
8 cumulative_sum = 0
9
10 # Iterate through the array with index and value
11 for current_index, num in enumerate(nums):
12 # Update cumulative sum
13 cumulative_sum += num
14
15 # Check if (cumulative_sum - k) exists in the map
16 # If it exists, we found a subarray with sum k
17 target_sum = cumulative_sum - k
18 if target_sum in sum_to_index:
19 # Calculate the length of subarray and update max_length
20 subarray_length = current_index - sum_to_index[target_sum]
21 max_length = max(max_length, subarray_length)
22
23 # Store the first occurrence of this cumulative sum
24 # This ensures we get the maximum length subarray
25 if cumulative_sum not in sum_to_index:
26 sum_to_index[cumulative_sum] = current_index
27
28 return max_length
29
1class Solution {
2 public int maxSubArrayLen(int[] nums, int k) {
3 // HashMap to store cumulative sum and its earliest index
4 // Key: cumulative sum, Value: earliest index where this sum occurs
5 Map<Long, Integer> sumToIndexMap = new HashMap<>();
6
7 // Initialize with sum 0 at index -1 (before array starts)
8 // This handles subarrays starting from index 0
9 sumToIndexMap.put(0L, -1);
10
11 // Variable to track the maximum subarray length
12 int maxLength = 0;
13
14 // Running cumulative sum
15 long cumulativeSum = 0;
16
17 // Iterate through the array
18 for (int currentIndex = 0; currentIndex < nums.length; ++currentIndex) {
19 // Update cumulative sum
20 cumulativeSum += nums[currentIndex];
21
22 // Check if (cumulativeSum - k) exists in map
23 // If it exists at index j, then sum from (j+1) to currentIndex equals k
24 // Length would be currentIndex - j
25 long targetSum = cumulativeSum - k;
26 if (sumToIndexMap.containsKey(targetSum)) {
27 int subarrayLength = currentIndex - sumToIndexMap.get(targetSum);
28 maxLength = Math.max(maxLength, subarrayLength);
29 }
30
31 // Store the earliest occurrence of this cumulative sum
32 // Only add if this sum hasn't been seen before (to keep earliest index)
33 sumToIndexMap.putIfAbsent(cumulativeSum, currentIndex);
34 }
35
36 return maxLength;
37 }
38}
39
1class Solution {
2public:
3 int maxSubArrayLen(vector<int>& nums, int k) {
4 // HashMap to store the first occurrence index of each prefix sum
5 // Initialize with {0, -1} to handle subarrays starting from index 0
6 unordered_map<long long, int> prefixSumToIndex{{0, -1}};
7
8 int maxLength = 0;
9 long long currentPrefixSum = 0;
10
11 // Iterate through the array to find the maximum subarray length
12 for (int i = 0; i < nums.size(); ++i) {
13 // Calculate the running prefix sum
14 currentPrefixSum += nums[i];
15
16 // Check if there exists a prefix sum such that
17 // currentPrefixSum - (that prefix sum) = k
18 // This means the subarray between them has sum k
19 long long targetPrefixSum = currentPrefixSum - k;
20 if (prefixSumToIndex.count(targetPrefixSum)) {
21 // Update the maximum length if we found a longer subarray
22 int subarrayLength = i - prefixSumToIndex[targetPrefixSum];
23 maxLength = max(maxLength, subarrayLength);
24 }
25
26 // Store the first occurrence of this prefix sum
27 // We only store the first occurrence to maximize the subarray length
28 if (!prefixSumToIndex.count(currentPrefixSum)) {
29 prefixSumToIndex[currentPrefixSum] = i;
30 }
31 }
32
33 return maxLength;
34 }
35};
36
1/**
2 * Finds the maximum length of a subarray that sums to k
3 * @param nums - The input array of numbers
4 * @param k - The target sum
5 * @returns The maximum length of a subarray with sum equal to k
6 */
7function maxSubArrayLen(nums: number[], k: number): number {
8 // Map to store the first occurrence index of each prefix sum
9 const prefixSumToIndex: Map<number, number> = new Map();
10
11 // Initialize with sum 0 at index -1 (before array starts)
12 // This handles the case when the subarray starts from index 0
13 prefixSumToIndex.set(0, -1);
14
15 // Variable to track the maximum subarray length found
16 let maxLength = 0;
17
18 // Running sum of elements (prefix sum)
19 let currentSum = 0;
20
21 // Iterate through each element in the array
22 for (let i = 0; i < nums.length; i++) {
23 // Add current element to the running sum
24 currentSum += nums[i];
25
26 // Check if there exists a prefix sum such that
27 // currentSum - prefixSum = k (which means the subarray sums to k)
28 const targetPrefixSum = currentSum - k;
29
30 if (prefixSumToIndex.has(targetPrefixSum)) {
31 // Calculate the length of the subarray and update maximum if needed
32 const subarrayLength = i - prefixSumToIndex.get(targetPrefixSum)!;
33 maxLength = Math.max(maxLength, subarrayLength);
34 }
35
36 // Store the first occurrence of this prefix sum
37 // We only store the first occurrence to maximize the subarray length
38 if (!prefixSumToIndex.has(currentSum)) {
39 prefixSumToIndex.set(currentSum, i);
40 }
41 }
42
43 return maxLength;
44}
45
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 using the enumerate function, and for each element, it performs constant-time operations: calculating the cumulative sum, checking if s - k
exists in the dictionary, updating the maximum length, and potentially adding a new entry to the dictionary. Dictionary operations (lookup and insertion) are O(1)
on average.
The space complexity is O(n)
, where n
is the length of the array nums
. In the worst case, when all cumulative sum values are unique, the dictionary d
will store up to n + 1
entries (including the initial entry {0: -1}
plus one entry for each element in the array). The remaining variables (ans
, s
, i
, x
) use constant space.
Common Pitfalls
Pitfall 1: Overwriting Earlier Prefix Sum Indices
The Problem: A critical mistake is updating the hash table every time we encounter a prefix sum, even if we've seen it before:
# WRONG approach
for current_index, num in enumerate(nums):
cumulative_sum += num
if cumulative_sum - k in sum_to_index:
subarray_length = current_index - sum_to_index[cumulative_sum - k]
max_length = max(max_length, subarray_length)
# This overwrites earlier occurrences - MISTAKE!
sum_to_index[cumulative_sum] = current_index
Why it's wrong: When we overwrite an earlier index with a later one, we lose the opportunity to find longer subarrays. For example, with nums = [1, 1, 0]
and k = 1
:
- At index 0: cumulative_sum = 1, store {1: 0}
- At index 1: cumulative_sum = 2, check for sum 1 (found at index 0), length = 1
- At index 2: cumulative_sum = 2 again
- If we overwrite: {2: 2} replaces {2: 1}
- This prevents us from finding longer subarrays later
The Solution: Only store the first occurrence of each prefix sum:
# CORRECT approach if cumulative_sum not in sum_to_index: sum_to_index[cumulative_sum] = current_index
Pitfall 2: Forgetting the Base Case
The Problem: Not initializing the hash table with {0: -1}
:
# WRONG - missing base case sum_to_index = {} # Should be {0: -1}
Why it's wrong: This misses subarrays that start from index 0. For example, with nums = [1, 2]
and k = 3
:
- The entire array sums to 3, but without the base case, we can't detect it
- We need
{0: -1}
to handle whencumulative_sum == k
The Solution: Always initialize with the base case:
sum_to_index = {0: -1}
Pitfall 3: Incorrect Length Calculation
The Problem: Using the wrong formula for calculating subarray length:
# WRONG calculations subarray_length = current_index - sum_to_index[target_sum] - 1 # Too short subarray_length = current_index - sum_to_index[target_sum] + 1 # Too long
Why it's wrong: The subarray spans from sum_to_index[target_sum] + 1
to current_index
inclusive. The correct length is the difference between these indices.
The Solution: Use the correct formula:
subarray_length = current_index - sum_to_index[target_sum]
Example Demonstrating All Fixes:
Consider nums = [1, -1, 5, -2, 3]
and k = 3
:
With correct implementation:
- Initialize:
{0: -1}
, max_length = 0, sum = 0 - Index 0 (val=1): sum = 1, check for -2 (not found), store {0: -1, 1: 0}
- Index 1 (val=-1): sum = 0, check for -3 (not found), 0 already exists (don't overwrite)
- Index 2 (val=5): sum = 5, check for 2 (not found), store {0: -1, 1: 0, 5: 2}
- Index 3 (val=-2): sum = 3, check for 0 (found at -1), length = 3 - (-1) = 4
- Index 4 (val=3): sum = 6, check for 3 (not found), store {0: -1, 1: 0, 5: 2, 3: 3, 6: 4}
Result: Maximum length = 4 (subarray [1, -1, 5, -2])
Which type of traversal does breadth first search do?
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!