Prefix Sums
Precompute a running total once, then answer "what is the sum between here and there?" instantly by subtracting two cumulative values. The same idea as cumulative P&L on a returns series, and it pairs with a hash map to count subarrays with a target sum in one pass.
Prerequisites: Arrays and Two Pointers
A prefix sum (or cumulative sum) is a precomputed running total of an array. You build it once, walking left to right and adding as you go, and in exchange you can then answer "what is the sum of the slice from position l to position r?" in a single subtraction, no matter how long the slice is. That turns a stream of range-sum questions from each into each, after one setup.
If you have ever plotted cumulative P&L or a cumulative-return curve, you have already built a prefix sum: the equity curve is the running total of the period returns, and the return earned between two dates is just the difference of the two cumulative values. The coding technique and the trading calculation are the same object.
The definition
Define so that is the sum of the first elements of the array :
The leading is a convenience that removes edge cases. With this, the sum of the elements from index up to (but not including) index is
In words: the total up to r minus the total up to l leaves exactly the piece in between. Everything before l cancels. Building is one pass ( time, space); every range query after that is one subtraction ().
Worked example: range sums
For a = [2, 4, 1, 5, 3, 2], the prefix array is P = [0, 2, 6, 7, 12, 15, 17].
- Sum of
a[1..3](the elements 4, 1, 5): . - Sum of the whole array: .
- Sum of
a[4..5](3, 2): .
def build_prefix(a): # O(n)
P = [0] * (len(a) + 1)
for i, x in enumerate(a):
P[i + 1] = P[i] + x
return P
def range_sum(P, l, r): # inclusive l..r, O(1)
return P[r + 1] - P[l]
The power move: count subarrays with sum k
The technique that makes prefix sums an interview staple is pairing them with a hash map. To count how many contiguous subarrays sum to exactly k, note that a subarray a[i..j] sums to k precisely when , i.e. when . So as you sweep the prefix values, keep a running tally of how many times each prefix value has occurred, and for each new prefix ask "how many earlier prefixes equal current − k?"
def subarrays_with_sum(a, k): # O(n) time, O(n) space
from collections import defaultdict
count = defaultdict(int)
count[0] = 1 # empty prefix
running = total = 0
for x in a:
running += x
total += count[running - k] # earlier prefixes that close a k-sum
count[running] += 1
return total
For a = [3, 4, 7, 2, -3, 1, 4, 2] and k = 7: the subarrays [3,4], [7], [7,2,-3,1], [1,4,2], and [4,... ] combinations are all found in one pass, , instead of the you would pay checking every subarray. This handles negative numbers too, which is exactly where a sliding window fails.
A range sum is the difference of two prefix values: . Build once in , then every range query is a single subtraction in .
Prefix sum plus a hash map counts subarrays with a target sum in , and it works with negatives (where sliding windows break). The trick: a subarray sums to k iff two prefix values differ by k, so tally how many earlier prefixes equal current − k.
Extensions and pitfalls
- 2D prefix sums (the "integral image") let you sum any rectangle of a matrix in after setup, useful for image and grid problems.
- Difference arrays are the mirror trick: to apply many range updates cheaply, add at the start index and subtract past the end, then prefix-sum once at the end.
- Off-by-one is the whole battle. Fix a convention, the half-open
P[r] - P[l]with a leading zero is the cleanest, and stick to it. Mixing inclusive and exclusive indices is the classic bug.
Prefix sums assume a static array. If values change between queries, a plain prefix array is to rebuild, so a stream of updates makes it slow, switch to a Fenwick tree (BIT) or segment tree for updates and queries. And watch the indexing: the leading exists precisely to keep the boundaries clean.
In quant-dev interviews prefix sums power range-sum queries, subarray-sum-equals-k, count-nice-subarrays, pivot-index, and 2D region sums. On the desk the same structure is your cumulative-return curve and cumulative P&L, and it is the first step before scanning that curve for the peak-to-trough drawdown.
Related concepts
Practice in interviews
Further reading
- Cormen, Leiserson, Rivest & Stein, Introduction to Algorithms (ch. 15)
- Laaksonen, Competitive Programmer's Handbook (ch. 9)