Prefix Sum Pattern: Algorithmic Intuition, Sub-patterns & C++ Guide
Store running totals so the sum of any range becomes one subtraction: sum(l..r) = prefix[r+1] − prefix[l].
5 sub-patterns · 18 questions
What is this pattern?
Prefix Sum means storing running totals so you never add up the same numbers twice.
prefix[i] is the sum of the first i elements, with prefix[0] = 0. Then the sum of any range is one subtraction:
sum(l..r) = prefix[r + 1] − prefix[l]
There are two ways to use it:
- Answer range queries: build
prefixonce, then each "sum from l to r" costs O(1). - Search for a range: walk left to right keeping the running total. At index
i, ask "which earlier total would make the range between us right?" and look it up in a hash map of totals you've already seen.
The second use is the powerful one. It works even with negative numbers, where a sliding window breaks.
Your bank statement shows the balance after every transaction. To know how much you spent between March 3 and March 10, you don't add up every transaction — you subtract the two balances. And if you want to know whether some stretch of days added up to exactly ₹0, you just check whether the same balance ever appeared twice.
See it move
prefix has n + 1 cells: prefix[i] sits just before nums[i]. The two blue cells are the only ones a query reads.
nums = [3, -2, 5, 1, -4, 6], queries = [1, 3], [0, 4], [2, 5]step 1/10- prefix[0]
- 0
prefix has one more cell than nums. prefix[i] is the total of everything before nums[i], so prefix[0] = 0.
Count subarrays with sum exactly 3. Gold prefix cells are the earlier totals that pair with the current one (blue).
nums = [1, 2, -1, 2, 1, -3, 3], k = 3step 1/9- k
- 3
- prefix
- 0
- count
- 0
Before reading anything, the running total is 0. Putting 0 in the map lets ranges that start at index 0 be found.
How do I recognise it?
Many range-sum questions on an array that never changes ("sum from l to r", "sum of a rectangle").
"Number of subarrays with sum exactly K" — especially when the array can have negative numbers or zeros.
"Longest subarray with equal number of 0s and 1s / X and Y" — turn one kind into −1 and look for a zero total.
"Sum divisible by K" or "sum is a multiple of K" — compare remainders of totals.
"For every index, combine something from its left and its right" (pivot index, product except self).
Looks like Prefix Sum, but isn't
With only positives, the window sum grows when you extend and shrinks when you move left, so a window is O(N) with O(1) space. Prefix + hash map also works but is heavier than needed.
One update changes every prefix after it — O(N) per update. Trees give O(log N) for both updates and queries.
Prefix sums are about contiguous ranges. A pair of separate elements is a different question.
You can do it with prefix sums (current prefix − smallest earlier prefix), but that is Kadane in disguise. No hash map is needed because there's no exact target to look up.
How would I discover it from scratch?
Don't memorise “use prefix sum”. Ask these questions in order — the pattern falls out.
Try every start l and every end r, and add up nums[l..r] — O(N³). Or keep a running sum per start — O(N²).
Sub-patterns
Same core idea, different rule for “valid”. Learn them in this order.
Static range sum
3 questionsThe data never changes, and you're asked for many range sums. Build the prefix array once (O(N)), then answer each question with one subtraction.
Exact sum + hash map
5 questionsFind (or count) contiguous ranges whose sum is exactly K. Walk left to right; at each index look up prefix − K in a map of earlier totals.
Prefix state / balance
3 questionsRecode each element so that "balanced" becomes "the same state as before": 0 → −1 and 1 → +1, or a bitmask of odd/even counts. Then the longest balanced range runs between the first and the latest time a state appears.
Modulo / divisibility
3 questionsA range sum is divisible by K exactly when the totals at its two ends leave the same remainder. Store remainders instead of totals.
Prefix + suffix
4 questionsEach index needs something from everything on its left and everything on its right. Make one pass from the left, one from the right, then combine.
Don't mix these up
All numbers positive and the condition is "at most / at least"? Sliding window. Exact sum, or negatives allowed? Prefix sum + hash map — with negatives, shrinking the window doesn't reliably make the sum smaller.
Counting ranges → count[total]++ every time. Longest range → save the index only the first time, so later matches stretch as far back as possible.
"Equals K" is a subtraction on the totals. "Divisible by K" ignores everything except the remainder — two totals match when a % k == b % k.
Sums can be undone by subtraction. Products can't be safely undone by division (zeros!), so keep the left part and the right part separately and multiply them.
Code templates (C++)
Build once, query in O(1)
The array has n + 1 cells. The leading 0 means a range starting at l = 0 needs no special case.
// Build once: prefix[i] = sum of the first i elements, prefix[0] = 0.
vector<long long> buildPrefix(const vector<int>& nums) {
vector<long long> prefix(nums.size() + 1, 0);
for (int i = 0; i < (int)nums.size(); i++)
prefix[i + 1] = prefix[i] + nums[i];
return prefix;
}
// Sum of nums[l..r] (inclusive) in O(1): everything up to r, minus everything before l.
long long rangeSum(const vector<long long>& prefix, int l, int r) {
return prefix[r + 1] - prefix[l];
}Count ranges with sum exactly K
Look up first, then insert. The map holds totals strictly before the current index, so the range is never empty.
// Count subarrays whose sum is exactly k. Works with negatives and zeros.
long long countSubarrays(vector<int>& nums, int k) {
unordered_map<long long, int> seen; // prefix total -> how many times it appeared
seen[0] = 1; // the empty prefix, for ranges starting at index 0
long long prefix = 0, count = 0;
for (int x : nums) {
prefix += x;
auto it = seen.find(prefix - k); // an earlier total that leaves exactly k between us
if (it != seen.end()) count += it->second;
seen[prefix]++; // insert AFTER the lookup
}
return count;
}Longest range with a balanced state
Store only the first index of each state. Seeing the same state again later means everything in between is balanced.
// Longest subarray with equal numbers of 0s and 1s.
// Recode 0 -> -1, 1 -> +1: "balanced" now means "same running total as before".
int longestBalanced(vector<int>& nums) {
unordered_map<int, int> first; // running total -> FIRST index where it appeared
first[0] = -1; // the empty prefix sits just before index 0
int balance = 0, best = 0;
for (int i = 0; i < (int)nums.size(); i++) {
balance += nums[i] == 1 ? 1 : -1;
auto it = first.find(balance);
if (it != first.end()) best = max(best, i - it->second);
else first[balance] = i; // never overwrite: the earliest index gives the longest range
}
return best;
}Divisible by K (remainders)
In C++, -3 % 5 is -3. The + k fix makes every remainder land in 0 … k − 1.
// Count subarrays whose sum is divisible by k.
// Two totals with the same remainder differ by a multiple of k.
long long countDivisible(vector<int>& nums, int k) {
vector<long long> count(k, 0); // remainder -> how many prefixes had it
count[0] = 1; // the empty prefix
long long prefix = 0, result = 0;
for (int x : nums) {
prefix += x;
int rem = ((prefix % k) + k) % k; // C++ % can be negative; this keeps it in 0..k-1
result += count[rem];
count[rem]++;
}
return result;
}Left pass + right pass
Often the right side doesn't need its own array: it's total − left − nums[i].
// For every index, combine what's on its left with what's on its right.
// Example: leftSum[i] - rightSum[i], without building a right array.
vector<long long> leftMinusRight(vector<int>& nums) {
long long total = 0;
for (int x : nums) total += x;
vector<long long> answer(nums.size());
long long left = 0; // sum of nums[0..i-1]
for (int i = 0; i < (int)nums.size(); i++) {
long long right = total - left - nums[i]; // sum of nums[i+1..n-1]
answer[i] = left - right;
left += nums[i];
}
return answer;
}