Subarray Sum Equals K

A subarray sums to k when a previous prefix equals current prefix minus k.

Approach

Track a running prefix sum and a map of how many times each prefix value has occurred. At each index, the number of subarrays ending here summing to k equals the count of prefix value (current − k) seen so far. Seed the map with {0: 1} for subarrays starting at index 0.

Time complexity

O(n)

Space complexity

O(n)

Common mistake

Forgetting the initial {0: 1} entry, which misses subarrays that start at the beginning.

Interactive walkthrough — step through it below, free and without an account

Problem

Given an integer array and a target value k, return how many contiguous, non-empty subarrays have sum exactly equal to k. The array may contain positive, zero, or negative values, so the solution must not rely on a sliding window.

Examples

Input: nums = [1, 2, 1, 2], k = 3

Output: 3

The matching subarrays are [1, 2] at indices 0–1, [2, 1] at indices 1–2, and [1, 2] at indices 2–3.

Input: nums = [1, -1, 1], k = 0

Output: 1

Only the subarray [1, -1] has sum 0.

Problem

Problem

Given an integer array and a target value `k`, return how many contiguous, non-empty subarrays have sum exactly equal to `k`. The array may contain positive, zero, or negative values, so the solution must not rely on a sliding window.

Problem and Goal

Learning goal: Recognize what must be counted and why a linear-time strategy is needed.

Core idea We need to count every contiguous range whose elements sum to k. A direct check of all subarrays can take quadratic time, which is too slow for large inputs. Because values may be negative, a sliding window cannot safely decide whether to expand or shrink.

Algorithm plan

  • Scan the array from left to right.
  • Maintain a running prefix sum called current.
  • Use a frequency map to remember how often each earlier prefix sum occurred.
  • Add the number of earlier prefixes equal to current - k.

Micro-example For nums = [1, 2, 1, 2] and k = 3, the answer is 3. The matching ranges are indices 0..1, 1..2, and 2..3.

Pitfalls

  • Do not assume all numbers are positive.
  • Count occurrences of a prefix, not just whether it exists.
  • Initialize the map before processing the first element.

Recap

  • The task is a counting problem over contiguous ranges.
  • Prefix sums turn each range sum into a difference of two prefixes.
  • Hashing makes the needed lookup constant time on average.

Problem and Goal

Count ranges, not just sums

Each valid subarray is identified by its start and end indices. Negative values break the monotonic behavior required by a sliding window, so we need prefix sums instead.

Turn every ending index into one lookup for earlier prefixes.

The Prefix-Sum Identity

Learning goal: Derive the exact prefix value that identifies a subarray ending at the current index.

Core idea Let prefix[j] be the sum of the first j elements. The sum from index i through index j - 1 is prefix[j] - prefix[i]. Therefore, a subarray ending just before the current prefix position sums to k exactly when an earlier prefix equals current - k.

Key insight

At each position, the number of new answers is the frequency of current - k among prefixes seen before this position.

Recurrence

For the current prefix sum current:

newMatches = frequency[current - k]

Then record the current prefix for future subarrays:

frequency[current] += 1

The ordering matters: query the earlier-prefix map first, then add the current prefix. The initial empty prefix has sum 0, so the map starts with frequency[0] = 1.

Micro-example Suppose current = 3 and k = 3. We need an earlier prefix of 0. That represents a subarray starting at index 0 and ending here.

Pitfalls

  • Using current + k instead of current - k reverses the equation.
  • Replacing frequencies with a set loses multiple valid starting points.
  • Updating the map before querying can incorrectly count an empty range when k = 0.

Recap

  • A range sum is a difference between two prefix sums.
  • Search for current - k.
  • Frequencies count how many valid starts produce each ending position.

The Prefix-Sum Identity

For target `k`, look up `current - k`.

Current 5 and `k = 3` means we need earlier prefix 2.

Main Approach

Learning goal: Translate the prefix-sum identity into a reliable one-pass algorithm.

Core idea The map represents all prefix sums strictly before the current position. At each element, extend the running prefix, ask how many earlier prefixes would make the difference equal k, and add that count to the answer. Only after the lookup do we store the new prefix.

Algorithm plan

  1. Set answer = 0 and current = 0.
  2. Seed frequency with {0: 1} for the empty prefix.
  3. For each value x in nums, update current += x.
  4. Add frequency.get(current - k, 0) to answer.
  5. Increment frequency[current].
  6. Return answer after the scan.
python
frequency = {0: 1}
current = answer = 0
for x in nums:
    current += x
    answer += frequency.get(current - k, 0)
    frequency[current] = frequency.get(current, 0) + 1
return answer

The map is a frequency table, not merely a membership set. Equal prefix sums can occur repeatedly, especially when zeros or positive and negative values cancel.

Micro-example For [1, 2, 1, 2], the running prefixes are 1, 3, 4, 6. At prefix 3, the needed value is 0, so the seeded entry finds [1, 2].

Pitfalls

  • Query before recording the current prefix.
  • Use an integer answer because many subarrays may match.
  • Preserve the full prefix sum; do not reset it when it exceeds k.

Recap

  • One pass maintains current, frequency, and answer.
  • Lookup precedes insertion.
  • The seed handles index-zero subarrays.

Main Approach

ApproachTimeWorks with negatives?Key issue
Enumerate every subarrayO(n^2)YesRepeats sum work
Sliding windowO(n)NoNeeds nonnegative monotonic sums
Prefix sums + frequency mapO(n)YesOne lookup per element

End-to-End Walkthrough

Learning goal: Trace every map lookup and update on one complete input.

Core idea We will use nums = [1, 2, 1, 2] and k = 3, the first example from the problem statement. The map always contains prefix sums from positions before the current element, along with how many times each sum has appeared.

Data structure model

The state has three parts:

StateMeaning
currentPrefix sum through the current index
frequency[s]Number of earlier prefixes equal to s
answerNumber of matching subarrays found so far

Start with current = 0, answer = 0, and frequency = {0: 1}. The first 0 is an artificial prefix before index 0; it represents choosing a subarray start immediately before the array.

Micro-example At index 1, current = 3. Since current - k = 0 and frequency[0] = 1, one new subarray ends at index 1: [1, 2]. Later, repeated prefix values can produce additional matches because each occurrence represents a distinct start.

Pitfalls

  • At index 0, do not forget that the seeded zero may create a match.
  • The map shown after each step includes the current prefix for future steps.
  • A single step may add zero, one, or several matches.

Recap

  • Every index contributes through one needed-prefix lookup.
  • The example produces additions of 0, 1, 1, and 1.
  • The final total is 3.

End-to-End Walkthrough

Step-by-step prefix-sum and frequency-map trace for the canonical example.

Variable trace
1 / 10
Seed {0: 1}: the empty prefix enables subarrays that start at index 0.
VariableValue
index-1
valuenull
current0
needednull
matchesAdded0
answer0
frequency{"0":1}

Action: seed map

This seed is essential, not optional.The map contains every prefix before the next element.
1 / 10

Pitfalls and Edge Cases

Learning goal: Prevent the initialization and ordering mistakes that most often break this solution.

Core idea The frequency map is a record of possible starting boundaries. It must include the boundary before index 0, and it must preserve repeated prefix sums. These details are what allow the same algorithm to handle zeros, negative numbers, and ranges beginning at the first element.

Data structure model

Think of each map entry as a boundary counter:

EntryInterpretation
frequency[0] = 1One empty prefix before the array
frequency[s] = cc earlier boundaries have prefix sum s
frequency[current - k]Number of new ranges ending now

Common pitfall

Forgetting the initial {0: 1} entry misses every valid subarray that starts at index 0.

For example, with [1, 2] and k = 3, the prefix becomes 3 at the second element. Without frequency[0] = 1, the algorithm reports zero instead of one.

Micro-example With nums = [1, -1, 1] and k = 0, repeated or changing prefixes must remain available. The first two values produce the valid range [1, -1].

Pitfalls

  • Do not use a set: repeated prefix sums represent different starts.
  • Do not clear the map when the running sum crosses k.
  • Do not insert before querying, especially when k = 0.
  • Use sufficiently wide integer arithmetic when the language requires it.

Recap

  • Seed {0: 1} before the loop.
  • Query first, then increment the current prefix frequency.
  • Negative values and zeros require no special cases.

Pitfalls and Edge Cases

Why must the frequency map start with {0: 1}?

Complexity and Wrap-up

Learning goal: Justify the O(n) time and O(n) space bounds and retain the reusable pattern.

Core idea The algorithm performs one constant-average-time hash-map lookup and one update for each array element. It never revisits a subarray or rescans a prefix, so the total work grows linearly with n. The map can store one entry for each distinct prefix sum.

Algorithm plan

  • Initialize current, answer, and frequency.
  • Scan each value once.
  • Query frequency[current - k].
  • Add the frequency to answer.
  • Record current for future positions.
  • Return answer.

Why it works

Every subarray is assigned to its ending index. At that index, its starting boundary appears exactly as a previously recorded prefix, so it is counted once and only once.

Micro-example For n = 4, the walkthrough makes four iterations and performs a bounded amount of map work per iteration. The map contains at most five prefix entries in that example, including the initial zero.

Pitfalls

  • Average O(1) map operations assume a well-behaved hash table.
  • Space is O(n) even if the final answer is small.
  • The target k does not need to be positive.

Recap

  • Time: O(n) average.
  • Space: O(n).
  • Reusable pattern: count earlier prefix sums equal to current - target.

Complexity and Wrap-up

Summarizes the per-element work and asymptotic resource usage.

Complexity analysis
Time
O(n)
Space
O(n)
Where the time goes
  • Scan the arrayEach element updates the running prefix once.dominantO(n)
  • Map lookup per elementLook up the frequency of current - k.O(1)
  • Map update per elementIncrement the current prefix frequency.O(1)
  • Store distinct prefixesAt most one new key can appear per element.dominantO(n)
How it grows
input size n →opsO(n²)O(n log n)O(n)O(log n)O(1)
Roughly what that costs
n101001,00010,000
O(n)101001K10K

A single pass with a frequency map avoids quadratic subarray enumeration while handling negative values correctly.

Now try it on your own problem

Paste any LeetCode problem — or one of your own — and codebrew builds a walkthrough like this one for it.

Build a lesson from any problem

Related problems