Prefix Sums

A prefix sum array stores, for each position, the sum of every element before it. With it, the sum of any contiguous range is one subtraction instead of a loop over the range. The same idea, with a hash map, counts the subarrays whose sum equals a target in O(n), including when the numbers are negative, which a sliding window cannot handle.

Every example on this page was produced by running the code shown.

The definition

For an array a of length n, the prefix sum array P has n + 1 entries:

P[0] = 0
P[i] = a[0] + a[1] + … + a[i-1]

P[i] is the sum of the first i elements. Building it is one pass: P[i+1] = P[i] + a[i].

The extra entry P[0] = 0, for the empty prefix, is what keeps the range formula free of special cases.

For a = [3, 1, 4, 1, 5, 9, 2, 6]:

i       0   1   2   3   4   5   6   7   8
a           3   1   4   1   5   9   2   6
P       0   3   4   8   9  14  23  25  31

The sum of a range

The sum of a[l .. r-1], the elements from index l up to but not including r, is:

sum(a[l .. r-1]) = P[r] - P[l]

P[r] is the sum of the first r elements, and P[l] is the sum of the first l. Subtracting removes the first l elements and leaves the ones from l to r - 1.

For a[2 .. 5], which is 4 + 1 + 5 + 9 = 19: P[6] - P[2] = 23 - 4 = 19.

After the O(n) build, every range sum costs O(1). Range Sum Query (303) asks exactly this: many range sums on an array that does not change. If the array does change between queries, each update would have to rewrite every later P[i], and a Fenwick tree or segment tree is used instead.

Subarray Sum Equals K (560)

The task: count the contiguous subarrays whose sum is exactly k. The numbers can be negative.

By the range formula, the subarray a[l .. r-1] sums to k when:

P[r] - P[l] = k        which is the same as        P[l] = P[r] - k

So for each end position r, the number of subarrays ending there with sum k is the number of earlier prefix sums equal to P[r] - k. A hash map counts how many times each prefix sum has appeared so far. The loop adds the count for P[r] - k, then records P[r]:

count = {0: 1}           # the empty prefix, P[0] = 0, seen once
prefix = answer = 0
for x in nums:
    prefix += x
    answer += count.get(prefix - k, 0)
    count[prefix] = count.get(prefix, 0) + 1

The starting entry {0: 1} is P[0]. Without it, subarrays that start at index 0 would never be counted.

Recording P[r] after looking up P[r] - k matters when k = 0. Looking it up after recording would count the empty subarray from r to r.

The full trace on [1, -1, 5, -2, 3] with k = 3 is in variable-size windows. The same page shows why a sliding window fails here: with negative numbers, removing an element from the window can raise its sum.

The hash map holds up to n + 1 prefix sums, so this is O(n) time and O(n) space.

Prefix products

Product of Array Except Self (238) uses the same idea with multiplication: a prefix product from the left and a suffix product from the right, multiplied together at each position. It is traced in array problems.

Products cannot be "subtracted" the way sums can. Dividing by a prefix product fails when it contains a 0. That is why problem 238 combines a prefix and a suffix instead of dividing.

Prefix sums on a grid

The same idea works in two dimensions. P[r][c] is the sum of every cell above and to the left of (r, c), that is, rows 0 .. r-1 and columns 0 .. c-1. It is built from three neighbours:

P[r+1][c+1] = grid[r][c] + P[r][c+1] + P[r+1][c] - P[r][c]

P[r][c+1] and P[r+1][c] both include the rectangle P[r][c], so it is subtracted once to avoid counting it twice.

The sum of the rectangle from (r1, c1) to (r2, c2), inclusive, is:

P[r2+1][c2+1] - P[r1][c2+1] - P[r2+1][c1] + P[r1][c1]

The two subtractions remove the strip above and the strip to the left. They overlap in the top-left corner, which was subtracted twice, so it is added back.

For this grid:

grid          P
3 0 1         0  0  0  0
5 6 3         0  3  3  4
1 2 0         0  8 14 18
              0  9 17 21

the rectangle from (1, 1) to (2, 2) holds 6 + 3 + 2 + 0 = 11, and the formula gives P[3][3] - P[1][3] - P[3][1] + P[1][1] = 21 - 4 - 9 + 3 = 11. Range Sum Query 2D (304) uses this.