Prefix-Sum Counts
What is this?
Imagine a shop till with a paper roll that prints the running total after every item, never the price of the item itself. Someone asks whether any stretch of purchases came to exactly £12. You do not re-add anything — you scan the roll for two totals that differ by 12. The receipt has quietly recorded every possible stretch, and all you needed was a fast way to look up "have I printed this total before?"
That is this entire chapter. The array is the receipt; the hash map is the fast lookup.
answer += count[S - k]"] B -->|"longest subarray"| D["map: total → FIRST index
answer = max(answer, j - first[S - k])"] C --> E["seed the map with {0: 1}"] D --> F["seed the map with {0: -1}"]
💡 Fun fact: Contiguous Array asks for the longest stretch with equal
0s and1s, which sounds nothing like arithmetic — until you map0 → −1. Now "equal counts" means "sums to zero", and a problem about balance becomes a problem about sums. This re-encoding is the same move physicists make when they redefine a zero point: nothing about the data changed, only where you measured from.
🔓 The 3 problems in this chapter are free. Sign in with Google or Microsoft to start solving.
The one-line idea: the sum of
(i, j]isS(j) − S(i), so "is there a window ending atjwith sumk?" is really "have I already seen the running totalS(j) − k?" — one map lookup, and the O(n²) window scan disappears.
1. The machine
Three lines of state and one decision:
running = 0
seen = {0: 1} # the empty prefix — see §5
answer = 0
for x in nums:
running += x
answer += seen.get(running - k, 0) # every earlier match ends a valid window here
seen[running] = seen.get(running, 0) + 1
The subtle part is the order of the two map operations. Look up before you record the current prefix. Record first and a zero-length window (k = 0) counts itself, inflating every answer by n.
And the fork that decides everything else:
| Question | Map value | Seed | Update rule |
|---|---|---|---|
"how many subarrays sum to k" |
running count | {0: 1} |
always increment |
"the longest subarray summing to k" |
first index seen | {0: -1} |
only if absent |
For "longest", overwriting is the bug. A prefix seen at index 2 and again at index 9 must keep the 2 — the earliest start is what makes the span long. For "how many", every occurrence counts, so you always increment.
2. Where you'll actually meet this
- Cumulative metrics. Prometheus counters, billing meters and page-view totals are stored as running sums precisely because the value over any interval is a subtraction. You are implementing the query side of that design.
- Financial reconciliation. "Did any run of transactions net to zero?" is Contiguous Array with balances instead of bits — the standard way to spot a wash trade or a duplicated-then-reversed charge.
- Streaming analytics. Counting windows that satisfy a property, in one pass, without buffering the whole stream.
- Databases. Window functions (
SUM(...) OVER (ORDER BY ...)) compute exactly this prefix column; the map is what an index gives you for free. - Genomics and signal processing. GC-content balance in a DNA read, or a signal segment whose deviations cancel, is the
0 → −1trick on a different alphabet.
3. A 30-second worked example
nums = [1, 0, 1, 0, 1], k = 2. How many subarrays sum to 2?
x=1 x=0 x=1 x=0 x=1
running S 0 1 1 2 2 3
S - k -2 -1 -1 0 0 1
seen has? - - - yes yes yes(×2)
new windows 0 0 0 +1 +1 +2 total = 4
map after each step:
{0:1} → {0:1,1:1} → {0:1,1:2} → {0:1,1:2,2:1} → … → {0:1,1:2,2:2,3:1}
The four windows are indices [0,2], [0,3], [1,4] and [2,4]. Note that you never enumerated one of them — the counts did. That is also why the final step contributes +2 in a single operation: prefix 1 had been seen twice, so two windows end at index 4.
4. Problems in this chapter
▶ Binary Subarrays With Sum
Count subarrays of a 0/1 array summing to goal. The machine in its plainest form — and a good place to notice that the sliding-window alternative needs the "at most k minus at most k−1" trick, while the map needs nothing clever.
Pattern: prefix sum → count. Target: O(n) time, O(n) space.
▶ Contiguous Array
The longest subarray with equal numbers of 0 and 1. Map 0 → −1 first, then it is "longest window summing to zero" — so the map stores first indices, seeded with {0: -1}.
Pattern: re-encode, then prefix sum → first index. Target: O(n) time, O(n) space.
▶ Maximum Size Subarray Sum Equals k
The general "longest window with sum k" over arbitrary integers, negatives included. This is why sliding windows do not work here — a window's sum is not monotonic when values can be negative, so shrinking on overshoot is invalid.
Pattern: prefix sum → first index. Target: O(n) time, O(n) space.
5. Common pitfalls 🚫
- Forgetting the seed. Without
{0: 1}, every window that starts at index 0 is invisible — and the sample input usually still passes, because most samples have a matching window somewhere in the middle. - Updating the map before the lookup. For
k = 0this counts each position as its own empty window. - Overwriting a first index. For "longest", write to the map only when the key is absent. Storing the latest index quietly shortens every answer.
- Using the wrong seed for "longest". It is
{0: -1}, not{0: 0}— the empty prefix sits before index 0, so a window covering the whole array computes asj − (−1). - Reaching for a sliding window with negatives present. Two pointers require monotonicity; negative values destroy it. The map does not care.
- Overflow in other languages. Running sums over 10⁵ elements of 10⁹ exceed 32 bits — use 64-bit outside Python.
6. Key takeaways
sum(i, j] = S(j) − S(i)— the whole chapter is this identity plus a fast lookup.- Count or first index, never both. "How many" increments; "how long" records the earliest and never overwrites.
- Seed with the empty prefix:
{0: 1}for counting,{0: -1}for longest. - Look up before you insert, or zero-length windows contaminate the count.
- Re-encode until it is a sum.
0 → −1turns balance into arithmetic, and the same trick generalises to any two-symbol "equal counts" question. - Negatives rule out sliding windows but leave the prefix-map approach untouched — knowing why is the senior answer.
Order: Binary Subarrays With Sum → Contiguous Array → Maximum Size Subarray Sum Equals k.
Contiguous Array
Core idea: Rewrite every
0as-1, then walk the array keeping a running balance (sum of the rewritten values). A subarray has equal 0s and 1s exactly when its-1s and+1s cancel — that is, when the balance is the same at both ends. So the moment two positions share a balance, the slice between them is balanced. Remember the first index at which each balance value appears; when that value returns at indexi, the lengthi - first[balance]is a candidate answer. Seed the map with{0: -1}so a prefix that's already balanced counts from the start.
The problem, rephrased
You're given a binary array nums containing only 0s and 1s. Find the length of the longest contiguous subarray that has an equal number of 0s and 1s. Return 0 if no such subarray exists.
Here's the mental flip that unlocks it. Counting "how many 0s vs how many 1s" over a window is awkward because you'd have to re-tally each window. Instead, score a 1 as +1 and a 0 as -1. Now "equal numbers of 0 and 1" becomes "the scores sum to zero" — and the sum of a subarray is just the difference of two running totals. Two prefixes with the same running total bracket a zero-sum slice between them.
A worked input/output
| nums | Output | Why |
|---|---|---|
[0,1] |
2 | One 0, one 1 — the whole array is balanced. |
[0,1,0] |
2 | Best is [0,1] (or [1,0]); you can't include all three (two 0s, one 1). |
[0,0,1,0,0,0,1,1] |
6 | The slice nums[2:8] = [1,0,0,0,1,1] has three 1s and three 0s. |
[1,1,1,1] |
0 | No 0s at all, so no balanced subarray exists. |
Notice the answer depends only on where a balance value first appeared, never on the local arrangement of bits.
Sign in to continue reading
The rest of this lesson is available with a free account. Signing in with Google or Microsoft is free.
Sign in to read the full lesson