Longest Subarray with Sum K

One of the most frequently asked array problems in coding interviews. Beginner-friendly breakdown with Python code, dry run, visualization, and everything you need to crack it in an interview.

To find the longest subarray with sum K, use a prefix sum with a HashMap. As you traverse the array, compute the running sum. If (current_sum − K) exists in the HashMap, update the maximum length. Store the first occurrence of each prefix sum. This gives an O(n) time, O(n) space solution that works even with negative numbers.

01: Introduction

Imagine you’re tracking your monthly expenses. You have a list of daily amounts — some days you spend money, some days you get cashback (negative). At the end, you want to find the longest streak of consecutive days where your net spending was exactly ₹15,000. That’s precisely the Longest Subarray with Sum K problem.

This problem shows up at companies like Google, Amazon, and Flipkart. It sounds simple but hides a trap that trips up a huge number of students: they reach for the sliding window technique without thinking, and it breaks entirely when negative numbers are in the array.

In this guide, you’ll understand exactly why sliding window fails here, when the prefix sum approach works, and how to implement it cleanly in Python with full confidence.

02: Problem Statement

Given an array arr[] of integers (which may include negative numbers, zeros, and positives) and an integer k, find the length of the longest subarray whose elements sum to exactly k. Return 0 if no such subarray exists.

✦ Example 1

Input: arr = [10, 5, 2, 7, 1, 9], k = 15

Output: 4

Why: The subarray [5, 2, 7, 1] (indices 1 to 4) has sum = 15, and it’s the longest one with that sum.

✦ Example 2

Input: arr = [1, -1, 5, -2, 3], k = 3

Output: 4

Why: The subarray [1, -1, 5, -2] (indices 0 to 3) has sum = 3. That’s 4 elements — the longest.

03: Input / Output Format

Field Description Example
arr List of integers (can be negative) [10, 5, 2, 7, 1, 9]
k Target sum (integer) 15
Output Length of the longest subarray with sum = k 4

04: Constraints

  • Array can have negative numbers, zeros, and positive numbers
  • Array length: 1 ≤ n ≤ 10⁵
  • Element values: -10⁴ ≤ arr[i] ≤ 10⁴
  • k can be any integer (negative, zero, or positive)
⚠️ Why Negatives Matter

The presence of negative numbers completely changes which algorithm you can use. With only positives, a shrinking window makes sense. With negatives, the sum can go up or down at any step, breaking window-based logic.

05: Naive Approach — O(n²)

The brute force idea is simple: try every possible subarray, compute its sum, and track the longest one that equals k.

1
Fix a starting index i from 0 to n-1.
2
Extend the ending index j from i to n-1.
3
Compute the sum of arr[i..j].
4
If sum equals k, update maximum length.
Python — Brute Force O(n²)
def longest_subarray_brute(arr, k):
n = len(arr)
max_len = 0
for i in range(n):
curr_sum = 0
for j in range(i, n):
curr_sum += arr[j]
if curr_sum == k:
max_len = max(max_len, j - i + 1)
return max_len

Problem: This is O(n²) time. For an array of 10⁵ elements, that’s 10¹⁰ operations — way too slow for any real interview or test.

06: Sliding Window DOES NOT Work Here

❌ Common Trap — Read This Carefully

Many students immediately jump to the sliding window technique because this looks like a “find subarray with target sum” problem. This is one of the most common mistakes in interviews.

Sliding window only works when the array contains strictly non-negative numbers. The logic behind it assumes: if your current window sum exceeds the target, shrinking from the left will reduce the sum. That logic breaks completely when negative numbers are present.

Concrete Failure Example

Consider: arr = [2, -1, 2], k = 3

The correct answer is 3 (entire array: 2 + (-1) + 2 = 3).

With a sliding window: when the sum exceeds k, you shrink from the left. But after removing 2 from the left, your window becomes [-1, 2] with sum = 1. You then expand — missing the correct answer entirely.

📌 Rule to Remember

Sliding window → only when all elements are non-negative.
Prefix sum + HashMap → works for all cases including negatives.

07: Optimal Approach — Prefix Sum + HashMap

The key insight is to use a running prefix sum and store the first time each prefix sum value was seen in a HashMap.

Core Idea

If the sum from index 0 to j is curr_sum, and you want sum from some index i to j to be exactly k, then:

sum(i to j) = curr_sum − prefix[i-1] = k

⟹ prefix[i-1] = curr_sum − k

So at every index, you just check: has the value (curr_sum − k) appeared before as a prefix sum? If yes, you’ve found a subarray with sum k — and its length is (current_index − index_where_that_prefix_sum_was_seen).

Algorithm Steps

1. Initialize a HashMap with {0: -1} (prefix sum 0 seen before the array starts, at index -1).
2. Traverse the array, maintaining a running curr_sum.
3. At each index, check if (curr_sum – k) exists in the HashMap.
4. If yes, compute length = current_index − hashmap[curr_sum – k] and update max.
5. If curr_sum is NOT already in the HashMap, store it with the current index. (First occurrence only.)

08: Key Intuition — Why This Works

Why store first occurrence only?

You want the longest subarray. If the same prefix sum appears at index 3 and later at index 7, and you find (curr_sum – k) at a later point, using index 3 gives you a longer subarray than using index 7. So always store the first time a prefix sum appears, and never update it.

Why initialize HashMap with {0: -1}?

If the entire subarray from the beginning (index 0 to j) sums to k, then curr_sum – k = 0. You need 0 to already be in the HashMap, and its index should be -1 so the length calculation j – (-1) = j + 1 works correctly.

Why does it work with negatives?

Unlike sliding window, this approach doesn’t try to shrink or grow a window based on the sum direction. It simply computes cumulative sums and looks for a difference — and subtraction works just as well whether numbers are positive or negative.

09: Prefix Sum Visualization

Let’s visualize how the prefix sum and HashMap evolve for arr = [10, 5, 2, 7, 1, 9], k = 15:

Array

10i=0
5i=1
2i=2
7i=3
1i=4
9i=5

Purple cells = the longest subarray [5, 2, 7, 1] with sum 15

Step-by-Step Prefix Sums

Index arr[i] curr_sum curr_sum−k In HashMap? Length HashMap State
0 {0: -1}
0 10 10 -5 No {0:-1, 10:0}
1 5 15 0 Yes (at -1) 1−(−1)=2 {0:-1, 10:0, 15:1}
2 2 17 2 No {0:-1,10:0,15:1,17:2}
3 7 24 9 No {…,24:3}
4 1 25 10 Yes (at 0) 4−0=4 ✓ MAX {…,25:4}
5 9 34 19 No {…,34:5}

Answer: 4 (maximum length tracked at index 4)

10: Flowchart

Longest Subarray with Sum K - Prefix Sum HashMap Visualization
11: Pseudocode

function longest_subarray_sum_k(arr, k):
hashmap = {0: -1}
curr_sum = 0
max_len = 0

for i from 0 to len(arr) - 1:
curr_sum = curr_sum + arr[i]

if (curr_sum - k) exists in hashmap:
length = i - hashmap[curr_sum - k]
max_len = max(max_len, length)

if curr_sum NOT in hashmap:
hashmap[curr_sum] = i

return max_len

12: Python Program


def longest_subarray_sum_k(arr, k):
# Store first occurrence of each prefix sum
# {0: -1} handles the case where subarray starts at index 0
prefix_map = {0: -1}

curr_sum = 0 # Running prefix sum
max_len = 0 # Track the longest valid subarray

for i, val in enumerate(arr):
curr_sum += val # Add current element to running sum

# If (curr_sum - k) was seen before,
# then arr[prev_index+1 .. i] has sum = k
if (curr_sum - k) in prefix_map:
length = i - prefix_map[curr_sum - k]
max_len = max(max_len, length)

# Only store first occurrence (for maximum length)
if curr_sum not in prefix_map:
prefix_map[curr_sum] = i

return max_len


# ——— Input Handling ———
if __name__ == "__main__":
arr = list(map(int, input("Enter array elements: ").split()))
k = int(input("Enter target sum k: "))
result = longest_subarray_sum_k(arr, k)
print(f"Longest subarray with sum {k} has length: {result}")

13: Dry Run

Input: arr = [10, 5, 2, 7, 1, 9], k = 15

Initial State: prefix_map = {0: -1}, curr_sum = 0, max_len = 0

i arr[i] curr_sum curr_sum−k In Map? Length max_len prefix_map after
0 10 10 −5 No 0 {0:−1, 10:0}
1 5 15 0 Yes 1−(−1)=2 2 {…, 15:1}
2 2 17 2 No 2 {…, 17:2}
3 7 24 9 No 2 {…, 24:3}
4 1 25 10 Yes 4−0=4 4 {…, 25:4}
5 9 34 19 No 4 {…, 34:5}

Final Answer: 4 — The subarray [5, 2, 7, 1] (indices 1 to 4) sums to 15.

14: Line-by-Line Explanation

1
prefix_map = {0: -1} — We pre-load the HashMap with 0 seen at index -1. This is critical: if a subarray starting from index 0 sums to k, then curr_sum – k = 0 needs to be findable.
2
curr_sum += val — Update the running prefix sum with each new element.
3
if (curr_sum – k) in prefix_map — This is the magic check. If we saw prefix sum curr_sum – k at some earlier index, then the portion between that index and now sums to exactly k.
4
length = i – prefix_map[curr_sum – k] — The length of the subarray from (prev_index + 1) to i.
5
if curr_sum not in prefix_map — Only store the FIRST occurrence of a prefix sum. Never overwrite. This ensures we always get the maximum possible length.

15: Time & Space Complexity

Time Complexity
O(n)

Single pass through the array. HashMap lookups are O(1) average.

Space Complexity
O(n)

The HashMap stores at most n distinct prefix sums.

Method Time Space Handles Negatives?
Brute Force O(n²) O(1) Yes
Sliding Window O(n) O(1) No
Prefix Sum + HashMap O(n) O(n) Yes ✓

16: Test Cases

# Input k Expected Notes
1 [10, 5, 2, 7, 1, 9] 15 4 Standard case
2 [1, -1, 5, -2, 3] 3 4 With negatives
3 [1, 2, 3] 10 0 No valid subarray
4 [5] 5 1 Single element
5 [0, 0, 0, 0] 0 4 All zeros
6 [1, 2, 1, 2, 1, 2] 3 2 Multiple subarrays, returns longest

17: Edge Cases

Empty Array

If arr = [], the loop never runs and we return 0. Always safe.

All Zeros, k = 0

Every prefix sum will be 0. Since we store first occurrence only (at -1), every index will trigger a match and the entire array will be the answer. Correct behavior.

Multiple Longest Subarrays

The algorithm naturally handles this. Whichever match gives the larger length wins — all comparisons are done with max(), and we return the final maximum.

k = 0

The algorithm works perfectly. If curr_sum – 0 = curr_sum was seen before, that subarray has sum 0. The initial {0: -1} handles the case where the array from index 0 sums to 0.

18: Common Interview Questions

  1. Why doesn’t sliding window work for this problem when negatives are present? — Because shrinking the window doesn’t guarantee a smaller sum when negatives exist. The monotonic assumption breaks.
  2. Why do you initialize the HashMap with {0: -1}? — To handle subarrays that start from index 0. Without it, we’d miss the case where the prefix sum itself equals k.
  3. Why store only the first occurrence of each prefix sum? — We want the maximum length. Using a later occurrence of the same prefix sum would give a shorter subarray.
  4. What is the time complexity, and why? — O(n). We make one pass through the array, and each HashMap operation is O(1) average case.
  5. What happens if the same prefix sum appears multiple times? — We skip updating the HashMap after the first occurrence. The first index gives the longest possible subarray ending at any future index.
  6. How would you modify this to return the subarray itself instead of just the length? — Track start and end indices: when you find a match, compute start = prefix_map[curr_sum – k] + 1 and end = i. Return arr[start:end+1].

19: Real-World Applications

💰 Financial Tracking

Given a record of daily transactions (positive = income, negative = expense), find the longest consecutive period where net cash flow equals a target value. Used in personal finance apps and bank analytics.

⏱️ Time Interval Analysis

In sensor data or server logs, finding the longest time window with a net change of exactly k helps detect patterns in usage, load balancing, and anomaly detection.

📡 Signal Processing

Finding the longest sequence of signal values that sum to a target amplitude is used in audio processing and wireless communications for pattern detection.

20: Mistakes Beginners Make

  • Using sliding window on arrays with negative numbers. It will produce wrong answers without any error — the most dangerous type of bug.
  • Not initializing the HashMap with {0: -1}. This causes the algorithm to miss subarrays that start from index 0.
  • Overwriting the HashMap when a prefix sum repeats. If you store the latest index instead of the first, you get shorter subarrays — wrong answer for “longest.”
  • Checking if curr_sum (not curr_sum − k) is in the map. The key check is always curr_sum – k, not curr_sum.
  • Computing length as i − prefix_map[curr_sum − k] − 1. The correct formula is i − prefix_map[curr_sum − k] (no −1). The prefix sum at index j was the sum of elements 0..j, so the next element starts at j+1.

 Approach Comparison

Approach Works for Negatives? Time Space Best Used When
Brute Force Yes O(n²) O(1) n ≤ 1000, quick check
Sliding Window No O(n) O(1) Only non-negative arrays
Prefix Sum + HashMap Yes ✓ O(n) O(n) General case — always safe

 Conclusion

Key Takeaways

The Longest Subarray with Sum K is a classic interview problem that tests whether you understand prefix sums deeply. The sliding window approach is a red herring when negative numbers are involved.

The optimal solution using Prefix Sum + HashMap runs in O(n) time, works for all types of arrays, and is built on a simple but powerful mathematical observation: if two prefix sums differ by k, the subarray between them sums to k.

Always initialize the map with {0: -1}, always store only the first occurrence of each prefix sum, and always check for curr_sum − k. Get these three things right and you’ll solve this problem every time.

Frequently Asked Questions

What is the Longest Subarray with Sum K problem?
It asks you to find the length of the longest contiguous subarray within an array whose elements sum to a given value k. The array can contain positive numbers, negative numbers, and zeros.
Can I use sliding window for this problem?
Only if the array contains exclusively non-negative numbers. If the array can contain negative numbers (which is the general case), sliding window will produce incorrect results. Use prefix sum + HashMap instead, which handles all cases.
Why do we initialize the HashMap with {0: -1}?
Because if the prefix sum at index i equals k, then curr_sum − k = 0. We need 0 to already be in the map, and its stored index (-1) makes the length calculation i − (−1) = i + 1, which is correct for a subarray from index 0 to i.
What if there are multiple subarrays with sum k?
The algorithm naturally handles this. It uses max() at every match, so the longest one among all valid subarrays is kept. The “first occurrence” storage ensures that when a prefix sum is found again, the oldest (and thus giving the longest span) index is used.
What is the time complexity of the prefix sum approach?
O(n) time — one pass through the array with O(1) HashMap operations. Space is O(n) for the HashMap storing up to n distinct prefix sums. This is optimal for this problem.
Does this algorithm work when k is negative or zero?
Yes. The algorithm makes no assumptions about the sign of k. When k = 0, it finds the longest subarray that sums to zero. When k is negative, it still correctly checks for (curr_sum − k) in the HashMap and handles negative prefix sums just fine.
What should I return if no subarray with sum k exists? 
Return 0. Since max_len is initialized to 0 and only updated when a valid subarray is found, it naturally returns 0 if the algorithm never finds a matching subarray.

pro.technaga.com
If you want more, follow
If you are more into programming, follow
If you want to see this in a good, stylish way, click this  longest-subarray-sum-k-python

 

Leave a Comment