Longest Subarray with Sum
Problem Overview
| Attribute | Value |
|---|---|
| Difficulty | Medium |
| Category | Sliding Window / Prefix Sum |
| Time Limit | 1 second |
| Key Technique | Two Pointers (positive arrays) / Hash Map (general) |
| CSES Link | Subarray Sums I |
Learning Goals
After solving this problem, you will be able to:
- Apply sliding window technique for variable-size window problems
- Use prefix sums with hash maps for subarray sum problems
- Choose between sliding window and hash map based on constraints
- Handle edge cases involving empty subarrays and zero sums
Problem Statement
Problem: Given an array of integers and a target sum, find the length of the longest contiguous subarray that sums to the target value.
Input:
- Line 1: n (array size) and x (target sum)
- Line 2: n space-separated integers
Output:
- Length of longest subarray with sum equal to target (0 if none exists)
Constraints:
- 1 <= n <= 2 * 10^5
- 1 <= x <= 10^9
- 1 <= a[i] <= 10^9 (for sliding window variant)
- -10^9 <= a[i] <= 10^9 (for hash map variant)
Example
Input:
8 8
3 1 4 1 5 9 2 6
Output:
4
Explanation: The subarray [3, 1, 4] has sum 8 and length 3. The subarray [1, 4, 1, 2] = wait, let’s check: we need sum = 8. Subarray [3, 1, 4] = 8 with length 3. Subarray [1, 5, 2] is not contiguous. The longest valid subarray is [3, 1, 4] with length 3.
Intuition: How to Think About This Problem
Pattern Recognition
Key Question: When do we expand the window, and when do we shrink it?
For positive integers only: Use sliding window. When sum exceeds target, shrink from left. When sum equals target, record the length.
For arrays with negative numbers: Sliding window does not work because removing elements might increase or decrease the sum unpredictably. Use prefix sum + hash map instead.
Breaking Down the Problem
- What are we looking for? Maximum length subarray with exact sum
- What information do we have? Array elements and target sum
- What is the relationship? Subarray sum = prefix[j] - prefix[i-1]
Analogies
Think of this like a worm crawling along a number line. The worm’s body (window) expands and contracts to maintain a specific “weight” (sum). For positive-only arrays, the worm knows that stretching always adds weight and shrinking always reduces it.
Solution 1: Sliding Window (Positive Arrays Only)
Key Insight
The Trick: With positive numbers, window sum is monotonic - expanding increases sum, shrinking decreases sum.
Algorithm
- Initialize left pointer at 0, current_sum at 0, max_length at 0
- Expand right pointer through the array
- Add arr[right] to current_sum
- While current_sum > target, shrink by moving left pointer right
- If current_sum == target, update max_length
- Return max_length
Dry Run Example
Let’s trace through with input arr = [2, 1, 5, 1, 3, 2], target = 8:
Initial state:
left = 0, current_sum = 0, max_length = 0
Step 1: right = 0, arr[0] = 2
current_sum = 0 + 2 = 2
2 < 8, no shrink needed
2 != 8, don't update max_length
Window: [2], sum = 2
Step 2: right = 1, arr[1] = 1
current_sum = 2 + 1 = 3
3 < 8, no shrink needed
Window: [2, 1], sum = 3
Step 3: right = 2, arr[2] = 5
current_sum = 3 + 5 = 8
8 == 8, update max_length = 3
Window: [2, 1, 5], sum = 8
Step 4: right = 3, arr[3] = 1
current_sum = 8 + 1 = 9
9 > 8, shrink: subtract arr[0]=2, left=1, sum=7
7 < 8, stop shrinking
Window: [1, 5, 1], sum = 7
Step 5: right = 4, arr[4] = 3
current_sum = 7 + 3 = 10
10 > 8, shrink: subtract arr[1]=1, left=2, sum=9
9 > 8, shrink: subtract arr[2]=5, left=3, sum=4
Window: [1, 3], sum = 4
Step 6: right = 5, arr[5] = 2
current_sum = 4 + 2 = 6
Window: [1, 3, 2], sum = 6
Final: max_length = 3 (subarray [2, 1, 5])
Visual Diagram
Array: [2, 1, 5, 1, 3, 2] Target: 8
left=0 right=2
| |
v v
[2, 1, 5] 1, 3, 2
└────────┘
sum = 8, length = 3
Code
Python:
def longest_subarray_positive(arr, target):
"""
Sliding window for positive-only arrays.
Time: O(n), Space: O(1)
"""
n = len(arr)
left = 0
current_sum = 0
max_length = 0
for right in range(n):
current_sum += arr[right]
# Shrink window while sum exceeds target
while current_sum > target and left <= right:
current_sum -= arr[left]
left += 1
# Check if we found target sum
if current_sum == target:
max_length = max(max_length, right - left + 1)
return max_length
Complexity
| Metric | Value | Explanation |
|---|---|---|
| Time | O(n) | Each element visited at most twice (once by right, once by left) |
| Space | O(1) | Only tracking pointers and sum |
Solution 2: Hash Map (General - Works with Negatives)
Key Insight
The Trick: If prefix[j] - prefix[i] = target, then subarray (i, j] has sum = target. Store first occurrence of each prefix sum to maximize length.
Algorithm
- Initialize hash map with {0: -1} (empty prefix at index -1)
- Compute running prefix sum
- For each index, check if (prefix_sum - target) exists in map
- If found, compute length and update max
- Store first occurrence of each prefix sum
Dry Run Example
Let’s trace through with arr = [-2, 1, -3, 4, -1, 2], target = 3:
Initial: prefix_map = {0: -1}, prefix_sum = 0, max_length = 0
i=0: prefix_sum = -2
Look for -2 - 3 = -5 (not found)
Store: {0: -1, -2: 0}
i=1: prefix_sum = -1
Look for -1 - 3 = -4 (not found)
Store: {0: -1, -2: 0, -1: 1}
i=2: prefix_sum = -4
Look for -4 - 3 = -7 (not found)
Store: {0: -1, -2: 0, -1: 1, -4: 2}
i=3: prefix_sum = 0
Look for 0 - 3 = -3 (not found)
0 already in map, don't overwrite (keep first occurrence)
i=4: prefix_sum = -1
Look for -1 - 3 = -4 (found at index 2!)
length = 4 - 2 = 2, max_length = 2
-1 already in map, don't overwrite
i=5: prefix_sum = 1
Look for 1 - 3 = -2 (found at index 0!)
length = 5 - 0 = 5, max_length = 5
Store: {0: -1, -2: 0, -1: 1, -4: 2, 1: 5}
Result: max_length = 5 (subarray [1, -3, 4, -1, 2])
Code
Python:
def longest_subarray_general(arr, target):
"""
Hash map approach for arrays with any integers.
Time: O(n), Space: O(n)
"""
prefix_map = {0: -1} # Handle subarrays starting at index 0
prefix_sum = 0
max_length = 0
for i, num in enumerate(arr):
prefix_sum += num
# Check if (prefix_sum - target) exists
if prefix_sum - target in prefix_map:
length = i - prefix_map[prefix_sum - target]
max_length = max(max_length, length)
# Store first occurrence only (for maximum length)
if prefix_sum not in prefix_map:
prefix_map[prefix_sum] = i
return max_length
Complexity
| Metric | Value | Explanation |
|---|---|---|
| Time | O(n) | Single pass through array |
| Space | O(n) | Hash map stores up to n prefix sums |
Common Mistakes
Mistake 1: Using Sliding Window with Negative Numbers
# WRONG - Does not work with negative numbers!
def wrong_approach(arr, target):
left = 0
current_sum = 0
for right in range(len(arr)):
current_sum += arr[right]
while current_sum > target: # May never trigger or wrong shrink
current_sum -= arr[left]
left += 1
Problem: Negative numbers break monotonicity. Shrinking might increase the sum! Fix: Use hash map approach for arrays with negative numbers.
Mistake 2: Overwriting Prefix Sum Index
# WRONG - Overwrites first occurrence
prefix_map[prefix_sum] = i # Always updates
# CORRECT - Keep first occurrence for maximum length
if prefix_sum not in prefix_map:
prefix_map[prefix_sum] = i
Problem: Overwriting gives shorter subarrays. Fix: Only store the first occurrence of each prefix sum.
Mistake 3: Forgetting Base Case {0: -1}
# WRONG - Missing base case
prefix_map = {} # Misses subarrays starting at index 0
# CORRECT
prefix_map = {0: -1} # Handles arr[0:j] subarrays
Problem: Cannot find subarrays that start at index 0. Fix: Initialize with {0: -1} to handle empty prefix.
Edge Cases
| Case | Input | Expected | Why |
|---|---|---|---|
| No solution | [1,2,3], target=10 |
0 | No subarray sums to 10 |
| Single element | [5], target=5 |
1 | Element itself is the answer |
| Entire array | [1,2,3], target=6 |
3 | Whole array is valid |
| Target = 0 | [1,-1,2,-2], target=0 |
4 | Sum of all elements is 0 |
| All same values | [2,2,2,2], target=4 |
2 | Multiple valid windows |
| Large values | [10^9, 10^9], target=2*10^9 |
2 | Use long long in C++ |
When to Use This Pattern
Use Sliding Window When:
- All array elements are positive (or all non-negative)
- You need O(1) space complexity
- Finding contiguous subarrays with sum constraints
Use Hash Map When:
- Array contains negative numbers
- Need to find exact sum (not “at most” or “at least”)
- Willing to trade O(n) space for simpler logic
Pattern Recognition Checklist:
- Contiguous subarray problem? Consider sliding window or prefix sum
- All positive elements? Sliding window works
- Contains negatives? Must use hash map
- Finding maximum length? Store first occurrence of prefix sum
- Finding minimum length? Store last occurrence of prefix sum
Related Problems
CSES Problems
| Problem | Link | Relationship |
|---|---|---|
| Subarray Sums I | CSES 1660 | Count subarrays (positive only) |
| Subarray Sums II | CSES 1661 | Count subarrays (with negatives) |
| Subarray Divisibility | CSES 1662 | Modular arithmetic variant |
| Maximum Subarray Sum | CSES 1643 | Kadane’s algorithm |
LeetCode Problems
| Problem | Difficulty | Key Difference |
|---|---|---|
| Subarray Sum Equals K | Medium | Count instead of max length |
| Minimum Size Subarray Sum | Medium | Minimum length, sum >= target |
| Maximum Size Subarray Sum Equals k | Medium | Same problem |
| Binary Subarrays With Sum | Medium | Binary array variant |
Key Takeaways
- Choose Your Weapon: Sliding window for positive arrays, hash map for general arrays
- Monotonicity Matters: Sliding window requires predictable sum behavior when expanding/shrinking
- First Occurrence: For maximum length, store first occurrence of each prefix sum
- Base Case: Always initialize with {0: -1} to handle subarrays starting at index 0
- Overflow: Use long long in C++ for large sums
Practice Checklist
Before moving on, make sure you can:
- Implement sliding window solution for positive arrays without looking
- Implement hash map solution for general arrays without looking
- Explain why sliding window fails with negative numbers
- Identify which approach to use based on constraints
- Handle all edge cases correctly