Subarray Sum Equals K

Subarray Sum Equals K looks easy at first. Just count the runs that add up to a number, right? But the trick is doing it in one pass. This question is a favorite because it tests whether you really understand prefix sums. That one idea turns a slow solution into a fast one.

🎯 The Problem

You get an array of numbers and a target number called k, and you count the parts that add up to k. Here are the rules.

  • Count how many continuous parts of the array add up to k.
  • A continuous part is called a subarray. It is a slice where the elements sit next to each other, with no gaps.
  • The numbers can be negative. So you cannot stop early just because a sum got big.

Let us say the array is [1, 1, 1] and k is 2. The slice [1, 1] at the start adds up to 2. The slice [1, 1] at the end also adds up to 2. So the answer is 2.

Input: nums = [1, 1, 1], k = 2
Output: 2
Explanation: nums[0..1] = 1 + 1 = 2, and nums[1..2] = 1 + 1 = 2

Here is a picture of the example. Each box is a number. The two highlighted windows are the subarrays that add up to k.

nums[0] = 1

nums[1] = 1

nums[2] = 1

window 0..1 sum = 2

window 1..2 sum = 2

🐢 Approach 1: Check Every Subarray (Brute Force)

The idea in one line: try every start and end pair and add up the numbers between them.

How it works:

  • The outer loop fixes the start.
  • The inner loop moves the end forward and keeps a running total.
  • Each time the total hits k, add one to the count.

Why it is weak:

  • For every start you scan forward to the end.
  • That is two nested loops over n items.
  • So the time grows as O(n²). Slow on a big array.

Here is the brute-force code for that idea:

subarray_sum_equals_k_brute_force.py
def subarray_sum(nums, k):
count = 0
for start in range(len(nums)):
total = 0
for end in range(start, len(nums)):
total += nums[end]
if total == k:
count += 1
return count
print(subarray_sum([1, 1, 1], 2))

🚶 Approach 2: Prefix Sum Scan (Better)

The idea in one line: keep a running total from each start so you skip re-adding the same numbers.

What a prefix sum is:

  • The running total from the start of the array to a spot is the prefix sum.
  • It saves you from adding the same stretch over and over.

How it works:

  • The outer loop still fixes the start.
  • The inner loop keeps a running total as the end moves forward.
  • Each time the running total equals k, add one to the count.

Why it is still not enough:

  • You drop the inner re-summing, which is a real saving.
  • But you still try every start and end pair.
  • So the time is still O(n²). We can do better in one pass.

Here is the prefix-sum scan code for that idea:

subarray_sum_equals_k_prefix_scan.py
def subarray_sum(nums, k):
prefix = [0]
for num in nums:
prefix.append(prefix[-1] + num)
count = 0
for start in range(len(nums)):
for end in range(start + 1, len(nums) + 1):
if prefix[end] - prefix[start] == k:
count += 1
return count
print(subarray_sum([1, 1, 1], 2))

⚡ Approach 3: Prefix Sum With a Hash Map (Best)

The idea in one line: count, in one pass, how many earlier prefix sums equal total - k.

The key insight:

  • A subarray ending at a spot sums to the prefix sum there minus the prefix sum just before it started.
  • So if the current prefix sum is total, a subarray ending here sums to k when some earlier prefix sum equals total - k.

How it works:

  • Store every prefix sum you have seen in a hash map. A hash map stores a key and a count and looks it up almost instantly.
  • Walk the array once. Add the current number to total.
  • Look up total - k in the map. Add its count to the answer.
  • Record the current total in the map.
  • Start the map with {0: 1}. That zero stands for the empty prefix, so a subarray that starts at index 0 is counted.

Why it is fast:

  • One pass over the array, with near-instant lookups.
  • So it runs in O(n) time. The map costs O(n) space.

This picture shows the dry run on [1, 1, 1] with k = 2. Follow the running total and the map.

start: map = {0:1}, total = 0, answer = 0

read 1: total = 1, need total-k = -1, not in map, answer = 0, store {0:1, 1:1}

read 1: total = 2, need total-k = 0, map has 0 once, answer = 1, store {0:1, 1:1, 2:1}

read 1: total = 3, need total-k = 1, map has 1 once, answer = 2, store {0:1, 1:1, 2:1, 3:1}

final answer = 2

Steps to Solve

  1. Create a hash map that maps a prefix sum to how many times it has appeared. Start it with {0: 1}.
  2. Set total to 0 and count to 0.
  3. Walk through the array one number at a time. Add the number to total.
  4. Look up total - k in the map. Add its stored count to count.
  5. Increase the stored count of total in the map by one.
  6. After the loop, return count.

This Python version uses a dictionary, which is Python’s built-in hash map.

subarray_sum.py
def subarray_sum(nums, k):
seen = {0: 1} # prefix sum -> how many times seen (empty prefix)
total = 0
answer = 0
for num in nums:
total += num # running prefix sum
if (total - k) in seen: # a matching earlier prefix exists
answer += seen[total - k]
seen[total] = seen.get(total, 0) + 1 # record this prefix sum
return answer
nums = [1, 1, 1]
k = 2
print(subarray_sum(nums, k))

The output of the above code will be:

2

Let us walk through the Python version line by line. Code first, then the why.

def subarray_sum(nums, k):
seen = {0: 1}
total = 0
answer = 0
for num in nums:
total += num
if (total - k) in seen:
answer += seen[total - k]
seen[total] = seen.get(total, 0) + 1
return answer

seen = {0: 1} starts the map with one prefix sum of zero. This stands for the empty prefix before the array begins. Without it, a subarray that starts at index 0 and sums to k would be missed. So this line is the quiet hero of the whole solution.

total += num updates the running prefix sum. After this line, total holds the sum of every number from the start up to the current one.

if (total - k) in seen: asks the key question. If some earlier prefix sum equals total - k, then the part between that point and here sums to exactly k. We are checking the map, not scanning the array. That is why it is fast.

answer += seen[total - k] adds the count. There may be several earlier positions that share the same prefix sum. Each one gives a valid subarray. So we add all of them, not just one.

seen[total] = seen.get(total, 0) + 1 records the current prefix sum for future steps. We add the current value after the lookup. That order matters. It stops a single element from pairing with itself.

⏱️ Time and Space Complexity

The brute force checks every subarray with two loops, so it is slow but uses almost no extra memory. The prefix sum with a hash map walks the array just once, so it is fast, but it needs extra memory to store the prefix sums it has seen. So you trade a little memory to save a lot of time. That trade takes the time from O(n²) down to O(n).

Approach Time Complexity Space Complexity
Brute force (nested loops) O(n²) O(1)
Prefix sum scan (running total) O(n²) O(1)
Prefix sum with hash map O(n) O(n)

Tip

The empty prefix {0: 1} is the part people forget. In an interview, say out loud why you seed the map with a zero. It shows you understand the math, not just the pattern.

🧩 Key Takeaways

  • ✅ A subarray sum equals the prefix sum at the end minus the prefix sum before it started.
  • ✅ For each prefix sum total, look for an earlier prefix sum equal to total - k.
  • ✅ Store prefix sums and their counts in a hash map for almost instant lookups.
  • ✅ Seed the map with {0: 1} so subarrays that start at index 0 are counted.
  • ✅ This works with negative numbers, where a sliding window would fail.

Check Your Knowledge

4 questions Show quiz Hide quiz

Test what you learned. Pick an answer for each question, then click Check.

  1. 1

    What is a subarray in this problem?

    Why: A subarray is a continuous slice, so the chosen elements must be next to each other with no gaps.

  2. 2

    For the current prefix sum total, what value do we look up in the map?

    Why: A subarray ending here sums to k when an earlier prefix sum equals total - k.

  3. 3

    Why do we start the map with {0: 1}?

    Why: The zero stands for the empty prefix, which lets a subarray starting at index 0 be counted.

  4. 4

    What is the time and space complexity of the prefix sum hash map solution?

    Why: One pass through the array is O(n) time, and storing prefix sums costs O(n) space.

🚀 What’s Next?