Prefix Sum
Precompute P[i] = a[0] + … + a[i-1] once so that any subarray sum a[l..r] is P[r+1] − P[l] in O(1).
Overview
A prefix sum array P of length n + 1 stores the running total of the input: P[0] = 0 and P[i] = P[i-1] + a[i-1]. After this O(n) pass, the sum of any contiguous range a[l..r] is P[r+1] − P[l], computed in O(1). It converts "many range-sum queries on a static array" from O(n) each to O(1) each, and it converts "find a subarray with sum k" into "find two prefix values that differ by k", which a Hash Map answers in one pass.
The same trick works for any invertible associative operation: sums, XOR (Prefix XOR), products modulo a prime (with modular inverses), counts of a property ("number of vowels up to i"). It does not work for max/min because there is no way to "subtract" the contribution of a prefix — that needs a Sparse Table or Segment Tree.
Intuition
A mental model before the formal terms.
Think of a car odometer. To know how far you drove between mile markers l and r, you do not re-measure the road — you read the odometer at both points and subtract. The odometer is the prefix sum: a single number at each position that already encodes everything before it.
For "subarray with sum k", the picture is the same odometer with a question: "was there an earlier reading exactly k less than the current one?" Keep the earlier readings in a hash map and the answer is one lookup per step.
How it works
- Allocate
Pof lengthn + 1withP[0] = 0. - For
ifrom1ton:P[i] = P[i-1] + a[i-1].P[i]is the sum of the firstielements. - Range query
sum(l, r)(inclusive, 0-based): returnP[r+1] − P[l]. - Subarray-sum-equals-
kcounting: scan left to right with a runningP; maintain a mapcount[value]of how many earlier prefixes equalvalue, seeded withcount[0] = 1. At each step addcount[P − k]to the answer, then incrementcount[P]. - Balance / pivot problems: "left sum equals right sum" at index
iisP[i] == total − P[i+1].
Why it works
Telescoping: P[r+1] − P[l] = (a[0] + … + a[r]) − (a[0] + … + a[l-1]) = a[l] + … + a[r]. Every element outside [l, r] appears in both terms and cancels. The P[0] = 0 sentinel makes l = 0 a special case of the same formula rather than an exception.
Hash-map counting: a subarray a[l..r] has sum k exactly when P[r+1] − P[l] = k, i.e. P[l] = P[r+1] − k. When the scan is at r, every l ≤ r has already been recorded in the map, so count[P[r+1] − k] is precisely the number of valid left endpoints for this right endpoint. Summing over all r counts every subarray exactly once. Seeding count[0] = 1 accounts for l = 0.
This works with negative numbers — unlike Sliding Window (Variable Size) — because it never relies on monotonicity; it only relies on subtraction being the inverse of addition.
Recognition
How to tell a problem wants this.
- "Sum of elements between indices `i` and `j`", "range sum query", "immutable" array, "many queries" —
qqueries withn, q ≤ 10^5makeO(n·q)impossible andO(n + q)the target. - "Subarray sum equals
k", "number of contiguous subarrays whose sum is divisible byk", "longest subarray with sumk" — especially when values can be negative (which rules out a sliding window). - "Pivot index", "equilibrium index", "split array into two parts with equal sum", "left sum equals right sum".
- "Count of
xin the range[l, r]", "number of 1s up to indexi" — prefix counts. - Any
O(n²)double loop over(l, r)whose inner work is summinga[l..r]— the prefix array collapses the inner sum.
Interactive visualization
Play, step, change the input. ← → and space work too.
1P[0] = 02for i in 0 .. n-1: P[i+1] = P[i] + a[i]3query(l, r) = P[r+1] - P[l]Pseudocode
1P = array of n + 1 zeros2for i in 1..n: P[i] = P[i-1] + a[i-1]3range_sum(l, r) = P[r+1] - P[l]4# count subarrays with sum k5seen = {0: 1}, run = 0, count = 06for x in a:7 run += x8 count += seen[run - k]9 seen[run] += 110return countImplementations
1# Subarray Sum Equals K: count contiguous subarrays summing to k (values may be negative)2from collections import defaultdict3 4 5def subarray_sum(a: list[int], k: int) -> int:61 · Map from prefix value to how many times it has occurred; seed the empty prefix7 seen: defaultdict[int, int] = defaultdict(int)8 seen[0] = 192 · Running prefix sum and answer10 run = 011 count = 012 for x in a:133 · Extend the prefix by the current element14 run += x154 · Every earlier prefix equal to run - k closes a subarray with sum k16 count += seen[run - k]175 · Record this prefix for later right endpoints18 seen[run] += 119 return countdefaultdict(int)returns 0 for missing keys, soseen[run - k]is a safe read-only lookup (it does insert a 0 entry, which is harmless but grows the dict).seen[0] = 1seeds the empty prefix.- Python ints are unbounded, so the prefix sum never overflows.
- Lookup before record — swapping the two lines breaks the
k == 0case. - Iterating
for x in areads each element once.
Every seen[run - k] read on a defaultdict inserts a 0 entry; use seen.get(run - k, 0) on a plain dict to avoid the extra entries.
itertools.accumulate(a)produces the prefix sums lazily;list(accumulate(a, initial=0))(3.8+) gives the sentinel-prefixed array for the range-query variant.Counterworks identically todefaultdict(int)here.- Big-int arithmetic is slower than fixed-width, but for prefix sums within 64 bits CPython uses a fast path.
- Using a plain dict with
seen[run - k]and getting aKeyError. - Building the prefix list with
sum(a[:i])in a loop — O(n^2). - Forgetting
seen[0] = 1.
- Prefix-sum width: C++ must key the map by
long long(anintprefix overflows silently into UB); JS/TS doubles are exact to 2^53 (about 9e15) — enough for n·max|a| up to ~1e15, beyond that useBigInt; Python ints never overflow. - Lookup semantics: C++
unordered_map::operator[]and Pythondefaultdictboth insert on read; C++findand Pythondict.get(k, 0)do not. JS/TSMap.getnever inserts. - Map keys: JS objects stringify numeric keys, so
Mapis required for correctness and speed; Python dicts and C++ unordered_map key on the number directly. - Building the prefix array: Python has
itertools.accumulate(a, initial=0); C++ hasstd::partial_sum/std::inclusive_scan; JS/TS need a manual loop (reducewith an accumulator array also works).
Complexity
O(n) build, O(1) per range query. The hash-map counting variant is O(n) time and O(n) space in a single pass.
Compare growth rates in the Complexity Explorer →When to use — and when not to
- Static array with many range-sum (or range-count, range-XOR) queries.
- Subarray problems with a sum/divisibility target where values may be negative.
- Balance/pivot problems comparing left-of-
iand right-of-itotals. - As a building block: 2D Prefix Sum, Difference Array, Kadane's Algorithm reformulated as
P[r] − min(P[l]).
- The array is updated between queries — each point update invalidates
O(n)prefix entries; use a Fenwick Tree or Segment Tree forO(log n)updates. - The query is max/min over a range — subtraction does not undo
max; use Sparse Table (static) or Segment Tree. - A single query on an array — a direct
O(n)scan is simpler and uses no extra memory. - Longest/shortest subarray under a monotone constraint with non-negative values — Sliding Window (Variable Size) does it in
O(1)space.
Alternatives
Common mistakes
- Using a length-
nprefix array withP[0] = a[0]and then special-casingl = 0everywhere; then + 1array withP[0] = 0removes the special case. - Computing
P[r] − P[l]instead ofP[r+1] − P[l](excludesa[r]). - Forgetting
seen[0] = 1in the counting variant — subarrays starting at index 0 are missed. - Incrementing
seen[run]before looking upseen[run − k]whenk = 0, which counts the empty subarray. - Overflow: prefix values grow to
n · max|a|; use 64-bit integers in Java/C++/Go. - For "divisible by
k" variants, forgetting to normalize negative remainders (((run % k) + k) % k).
Interview patterns
- Range Sum Query - Immutable (the
NumArrayclass). - Subarray Sum Equals K and Continuous Subarray Sum (prefix mod
kin a hash map). - Find Pivot Index / Product of Array Except Self (prefix and suffix products).
- Contiguous Array: map 0→−1, find the longest subarray with prefix sum 0 via first-occurrence map.
- Maximum Subarray as
max(P[r] − min_{l ≤ r} P[l])— an alternative derivation of Kadane's Algorithm. - Count of vowels / 1s in a range via a prefix count array.
- Recognizing the approach from an array and a targetIntermediate
- Deciding whether O(n²) can be improvedIntermediate
- Hash map or array?Beginner
- Average case versus worst caseIntermediate
- Minimum Size Subarray SumIntermediate
- Two SumBeginner
- Longest Substring Without Repeating CharactersIntermediate