Patterns/Prefix Sum

Prefix Sum

Top interview pattern
70 min read
Updated June 2026
What you'll learn
  • Build a prefix array and answer arbitrary range-sum queries in O(1)
  • Pair prefix sums with a hash map to count subarrays matching a target sum, even with negative values
  • Apply difference arrays for batch range updates at O(1) per update, then recover the final array in a single cumulative pass

The prefix sum pattern transforms O(n) range sum queries into O(1) operations by precomputing cumulative sums. Combined with hash maps, it handles subarray counting problems, including those with negative numbers, that sliding window cannot solve.

What Is the Prefix Sum Pattern?

Brute force computes a subarray sum by walking every element in the range. For a single query that's O(n). For q queries on an array of n elements, the total work is O(n × q). Prefix sum collapses each query to a single subtraction.

Precompute a running total. Given nums = [2, 4, 1, 3, 5], the prefix array stores cumulative sums: prefix = [0, 2, 6, 7, 10, 15]. The sum of elements 1 through 4 is prefix[5] - prefix[1] = 15 - 2 = 13. One arithmetic operation instead of iterating through four elements.

In general: sum(nums[l..r]) = prefix[r+1] - prefix[l]. O(n) to build, O(1) per query.

The leading zero matters. prefix[0] = 0 makes the formula work uniformly for every range, including those starting at index 0. Without it, those ranges need special handling. Allocate an array of length n+1 and set the first slot to zero.

Watch how the prefix array is built step by step:

Building the Prefix Array

Current Index
-
Formula
prefix[0] = 0
Status
INIT
nums[]2[0]4[1]1[2]3[3]5[4]prefix[]0[0]

Step 1 of 6 · Sentinel prefix[0] = 0 lets every range query use the same formula. No special case for ranges starting at index 0.

1nums = [2, 4, 1, 3, 5]
2
3# Building the prefix array (1-indexed convention)
4prefix = [0] # prefix[0] = 0, the sum of zero elements
5for num in nums:
6 prefix.append(prefix[-1] + num)
7
8# Result: prefix = [0, 2, 6, 7, 10, 15]
9# prefix[1] = 2 (sum of nums[0])
10# prefix[2] = 6 (sum of nums[0..1])
11# prefix[3] = 7 (sum of nums[0..2])
12# prefix[4] = 10 (sum of nums[0..3])
13# prefix[5] = 15 (sum of nums[0..4])
Key Insight
Every Range Query Is a Subtraction

prefix[j] - prefix[i] gives the sum of elements i through j-1. Precompute all running totals once in O(n), then answer any range query in O(1). When you see "subarray sum," think prefix subtraction. Declare your indexing convention upfront (e.g., 1-indexed prefix array of length n+1) to prevent off-by-one confusion during implementation.

The Core Formula

The formula sum(nums[l..r]) = prefix[r+1] - prefix[l] enables three major applications:

Range queries: The formula is applied directly: prefix[r+1] - prefix[l] gives the sum of elements l through r. No iteration needed.

Subarray sum = k: Rearrange prefix[j] - prefix[i] = k to prefix[i] = prefix[j] - k. At each position j, the algorithm checks whether prefix[j] - k has appeared before. This converts an O(n²) nested loop (checking every start-end pair) into an O(n) hash map lookup: for each endpoint, ask "does a matching start point exist?"

Divisibility by k: Two prefix sums with the same remainder mod k produce a subarray sum that is a multiple of k. The hash map tracks remainders instead of raw prefix values.

The Complexity Transformation

OperationBrute ForceWith Prefix Sum
PreprocessNoneO(n) once
Single range queryO(n)O(1)
m queriesO(m x n)O(n + m)

Range Sum Queries

With the 1-indexed convention, the range sum formula is clean and requires no special cases. The derivation follows step by step:

1# What does prefix[right + 1] contain?
2prefix[right + 1] = nums[0] + nums[1] + ... + nums[right]
3
4# What does prefix[left] contain?
5prefix[left] = nums[0] + nums[1] + ... + nums[left - 1]
6
7# When we subtract:
8prefix[right + 1] - prefix[left]
9 = (nums[0] + nums[1] + ... + nums[right])
10 - (nums[0] + nums[1] + ... + nums[left - 1])
11
12# The elements from 0 to left-1 cancel out:
13 = nums[left] + nums[left + 1] + ... + nums[right]
14
15# This is exactly sum(nums[left..right])
1def range_sum(prefix, left, right):
2 """Sum of nums[left..right] inclusive (1-indexed prefix)"""
3 return prefix[right + 1] - prefix[left]
4
5# Example: nums = [2, 4, 1, 3, 5], prefix = [0, 2, 6, 7, 10, 15]
6# range_sum(prefix, 1, 3)
7# = prefix[4] - prefix[1]
8# = 10 - 2
9# = 8
10# Verified: nums[1] + nums[2] + nums[3] = 4 + 1 + 3 = 8

Range Sum Query

Query Range
[1, 3]
prefix[4]
10
prefix[1]
2
Result
-
nums[]2[0]4[1]1[2]3[3]5[4]sum = 8prefix[]0[0]2[1]6[2]7[3]10[4]15[5]

Query: sum of nums[1..3]. We want 4 + 1 + 3 = 8

How to Recognize Prefix Sum Problems

Certain phrases in problem statements signal prefix sum applicability.

Common Problem Structures

"Subarray sum equals k" or "contiguous elements with sum" problems reduce to searching for a previous prefix sum with a specific value. At each position j, the algorithm asks: "has any earlier prefix sum equaled current_sum - k?" That question is a single hash map lookup, turning an O(n) scan of all earlier positions into an O(1) operation. The algebra (prefix[j] - prefix[i] = k rearranged to prefix[i] = prefix[j] - k) explains why it works, but the core move is the lookup.

"Range sum query" or "multiple queries" problems benefit from O(n) preprocessing followed by O(1) per query. Without preprocessing, m queries on n elements costs O(m x n).

"Count subarrays with property X" problems transform O(n²) iteration into O(n) hash map lookups. Instead of checking every possible subarray, the algorithm tracks prefix values and counts matches.

"Array may contain negative numbers" signals that sliding window won't work. Sliding window assumes shrinking the window decreases the sum; negatives violate this because removing a negative element increases the sum.

When Sliding Window Fits vs When It Doesn't

Sliding window relies on monotonicity: expanding the window increases the sum, and shrinking decreases it. This assumption enables greedy pointer movement. But when the array contains negative numbers, removing an element from the left can increase the sum (if that element was negative), breaking the greedy logic. The window might skip over valid solutions.

Prefix sum sidesteps this entirely by using pure algebra. The formula sum(l, r) = prefix[r+1] - prefix[l] works regardless of element signs. Instead of greedy pointer decisions, the algorithm looks up complements in a hash map.

Key Insight
Prefix Sum vs Sliding Window Decision

Negative numbers rule out sliding window for sum-based subarray problems. Shrinking the window can increase the sum when negative elements leave, which breaks the greedy movement rule.

  • Array has negative numbers: use prefix sum. The formula works regardless of sign.
  • "At most k" with non-negative values: sliding window is simpler and uses O(1) space.
  • "Exactly k" or "count all subarrays": prefix sum with a hash map.
  • Multiple range queries on a static array: build a prefix array once, then answer each query in O(1).

Which Template Should I Use?

Match the signal in the problem statement to the right technique:

Signal in the ProblemTechnique to Use
Range sum queries on a static arrayPrefix Sum Array (Template 1)
Count subarrays with sum = k (negatives possible)Prefix Sum + Hash Map (Template 2)
Divisibility by kPrefix Sum + Mod Map (Template 3)
"At most k" constraint, all positivesSliding Window instead
Multiple range updates, then queryDifference Array

The Three Prefix Sum Templates

Three templates cover most prefix sum problems. The choice depends on what the problem requires:

NeedTemplateData Structure
Single query, fixed range?Template 1: Direct subtractionPrefix array only
Count subarrays with sum = k? (negatives possible)Template 2: Hash mapPrefix array + hash map
Count subarrays divisible by k?Template 3: Modulo + hash mapHash map with remainders

Template 1: Basic Range Sum Query

Use this template for answering multiple range sum queries on a static array. Build the prefix array once, then answer each query in O(1).

1class PrefixSum:
2 def __init__(self, nums):
3 """O(n) preprocessing"""
4 self.prefix = [0]
5 for num in nums:
6 self.prefix.append(self.prefix[-1] + num)
7
8 def query(self, left, right):
9 """Sum of nums[left..right] inclusive, O(1)"""
10 return self.prefix[right + 1] - self.prefix[left]
11
12# Usage
13ps = PrefixSum([2, 4, 1, 3, 5])
14ps.query(1, 3) # Returns 8 (4 + 1 + 3)
15ps.query(0, 4) # Returns 15 (entire array)
16ps.query(2, 2) # Returns 1 (single element)

Complexity: O(n) preprocessing, O(1) per query, O(n) space.

Template 2: Subarray Sum Equals K

This template handles negative numbers.

The Derivation

Start from the core formula: every subarray sum is prefix[j] - prefix[i]. Finding subarrays with sum = k means finding pairs (i, j) where prefix[j] - prefix[i] = k.

1# We want: prefix[j] - prefix[i] = k
2# where i < j (i comes before j)
3
4# Rearranging algebraically:
5prefix[j] - prefix[i] = k
6prefix[j] - k = prefix[i] # subtract k from both sides
7prefix[i] = prefix[j] - k # flip the equation
8
9# At position j, we ask: "How many earlier positions i
10# have prefix[i] equal to (prefix[j] - k)?"
11# That's exactly what the hash map counts.

So at each position j, instead of iterating through all earlier positions, the algorithm looks up how many times the value prefix[j] - k has appeared. By storing all prefix sums in a hash map, each lookup becomes O(1) instead of O(n).

Implementation

1def subarray_sum_equals_k(nums, k):
2 """Count subarrays with sum exactly k. Works with negatives."""
3 prefix_count = {0: 1} # Base case: one way to have prefix sum 0
4 current_sum = 0
5 count = 0
6
7 for num in nums:
8 current_sum += num # This is prefix[j]
9 complement = current_sum - k # This is prefix[j] - k
10 count += prefix_count.get(complement, 0) # How many prefix[i] equal this?
11 prefix_count[current_sum] = prefix_count.get(current_sum, 0) + 1
12
13 return count

See the complete animated walkthrough of this algorithm in the Complete Walkthrough section below.

Why a Hash Map Instead of an Array?

Template 1 uses an array because indices go 0 to n. Template 2 uses a hash map because prefix sums can be any value (positive, negative, huge). A hash map stores only the sums that actually appear.

Why {0: 1}? The Base Case

The {0: 1} initialization is required, not a trick:

When does current_sum - k = 0? When current_sum = k. This means the subarray from index 0 to the current position has sum exactly k. The required "earlier prefix" is prefix[0] = 0, which represents "the sum before any elements" or "taking zero elements."

The {0: 1} says: "There is exactly one way to have a prefix sum of 0, which is taking zero elements from the start." This is the base case: the sum before any elements.

Common Mistake
Forgetting {0: 1} Silently Drops Subarrays
Forgetting {0: 1} silently misses every subarray that starts at index 0. If current_sum == k, the complement is 0, which must already be in the map. Without it, the lookup returns 0 and those valid subarrays are never counted. No error, no warning, just wrong answers.
Interview Tip
The {0: 1} initialization is the most common source of prefix sum bugs. State it explicitly when explaining the approach: "I initialize the map with {0: 1} to handle subarrays starting from index 0, since those have a complement of 0."

Template 3: Count Subarrays Divisible by K

Use this template when counting subarrays where the sum is divisible by k.

If prefix[j] % k == prefix[i] % k, then the difference prefix[j] - prefix[i] is divisible by k. Algebraically:

1# Same remainder mod k → difference is divisible by k
2# Example: prefix[3] = 14, prefix[1] = 4, k = 5
3# 14 % 5 = 4, 4 % 5 = 4 → same remainder
4# 14 - 4 = 10, which is divisible by 5

A concrete example: if prefix[3] = 14 and prefix[1] = 4 with k = 5, then 14 % 5 = 4 and 4 % 5 = 4. Same remainder. The subarray sum is 14 - 4 = 10, which is divisible by 5. The equal remainders guarantee the difference is a multiple of k.

So two prefixes with the same remainder mod k produce a subarray sum divisible by k. Track remainder counts instead of full prefix sums:

1def count_subarrays_divisible_by_k(nums, k):
2 """Count subarrays with sum divisible by k."""
3 remainder_count = {0: 1} # {0: 1} for subarrays from start
4 current_sum = 0
5 count = 0
6
7 for num in nums:
8 current_sum += num
9 remainder = current_sum % k
10
11 # CRITICAL: Handle negative remainders
12 if remainder < 0:
13 remainder += k
14
15 # Subarrays ending here with same remainder have divisible sum
16 count += remainder_count.get(remainder, 0)
17 remainder_count[remainder] = remainder_count.get(remainder, 0) + 1
18
19 return count
20
21# Example: nums = [4, 5, 0, -2, -3, 1], k = 5
22# Returns 7
Common Mistake
The Negative Remainder Trap
Python's % always returns a non-negative result when the divisor is positive, so -1 % 3 = 2 in Python. Java and C++ return a negative remainder instead: -1 % 3 = -1. Negative remainders don't match positive ones in a hash map, so the lookup silently fails. In Java or C++, normalize with ((x % k) + k) % k to guarantee a non-negative result.

The Difference Array

The difference array is the inverse of prefix sum: instead of fast queries on a static array, it enables fast range updates. Instead of iterating through every element in the range, you make just two point modifications.

Difference Array: Inverse of Prefix Sum

To add val to every element from index l to r (inclusive), instead of updating each element individually (O(n) per update), use two O(1) operations:

1def range_update(diff, l, r, val):
2 """Add val to all elements from index l to r inclusive"""
3 diff[l] += val # "Turn on" the effect at l
4 if r + 1 < len(diff):
5 diff[r + 1] -= val # "Turn off" the effect after r
6
7def reconstruct(diff):
8 """Convert difference array back to actual values via prefix sum"""
9 result = [0] * len(diff)
10 running = 0
11 for i in range(len(diff)):
12 running += diff[i]
13 result[i] = running
14 return result

Difference Array

original[]
0
0
0
0
0
0
diff[]
0
0
0
0
0
0
0

Start with an array of 6 zeros. We want to apply range updates efficiently.

The reconstruction step is exactly a prefix sum. The difference array records "changes" at boundaries, and prefix sum accumulates them into final values. This is why they are inverses: prefix_sum(difference_array(arr)) = arr.

Key Insight
Prefix Sum and Difference Array Are Inverses

Prefix sum converts point values into cumulative sums. Difference array converts range updates into point changes. Applying prefix sum to a difference array reconstructs the original. They undo each other, like addition and subtraction.

Interview Tip
The Difference Array Signal
If you see "apply k range updates, then query each position once," the difference array is the optimal approach. Applying each update by iterating through the range costs O(k * n). The difference array costs O(k + n): O(1) per update, then one O(n) prefix sum reconstruction.

Complete Walkthrough: Subarray Sum = K

Subarray Sum Equals K combines every prefix sum concept: the complement lookup, the {0: 1} base case, and the check-before-store ordering.

Problem Statement

Given an integer array nums and an integer k, return the total number of subarrays whose sum equals k. The array may contain negative numbers.

Step 1: Acknowledge the Brute Force

The brute force fixes a start index, then extends the end index while maintaining a running sum. That is O(n²) pairs, each answered in O(1), giving O(n²) total. The goal is to identify what structure the prefix sums share that lets us avoid examining every pair.

1# O(n²) brute force - DON'T code this, just mention it
2def brute_force(nums, k):
3 count = 0
4 for i in range(len(nums)):
5 running_sum = 0
6 for j in range(i, len(nums)):
7 running_sum += nums[j]
8 if running_sum == k:
9 count += 1
10 return count

Step 2: Recognize the Pattern

Every subarray sum is the difference of two prefix values: prefix[j] - prefix[i] = k. Rearranged: prefix[i] = prefix[j] - k. At each position j, the question becomes: how many earlier positions i have a prefix sum equal to prefix[j] - k? A hash map answers that in O(1) per position, reducing total work to O(n).

Step 3: Code the Optimal Solution

The implementation follows directly from Template 2. Initialize the map with {0: 1} to cover subarrays starting at index 0, check the complement before storing each new prefix, and return the count.

Step 4: Watch the Algorithm in Action

This diagram traces every step with nums = [1, 2, 3, -2, 5] and k = 3:

Subarray Sum = K (Hash Map Lookup)

current_sum
0
complement
-
count
0
k
3
nums[]1[0]2[1]3[2]-2[3]5[4]
prefix_count hash map
0: 1

Initialize hash map with {0: 1}. One way to have prefix sum 0 (taking zero elements)

Common Bugs in This Problem

Bug: Missing the base case

1# WRONG: Missing {0: 1}
2prefix_count = {} # Subarrays starting at index 0 are missed!
3current_sum = 0
4count = 0
5for num in nums:
6 current_sum += num
7 count += prefix_count.get(current_sum - k, 0)
8 prefix_count[current_sum] = prefix_count.get(current_sum, 0) + 1
9
10# RIGHT: Include the base case
11prefix_count = {0: 1} # "One way to have prefix sum 0"
12current_sum = 0
13count = 0
14for num in nums:
15 current_sum += num
16 count += prefix_count.get(current_sum - k, 0)
17 prefix_count[current_sum] = prefix_count.get(current_sum, 0) + 1

Bug: Store before check

1# WRONG: Store-before-check counts current position as "earlier"
2for num in nums:
3 current_sum += num
4 prefix_count[current_sum] = prefix_count.get(current_sum, 0) + 1 # Stored first!
5 count += prefix_count.get(current_sum - k, 0) # May match itself!
6
7# RIGHT: Check first, then store
8for num in nums:
9 current_sum += num
10 count += prefix_count.get(current_sum - k, 0) # Check first
11 prefix_count[current_sum] = prefix_count.get(current_sum, 0) + 1 # Then store
Common Mistake
Store-Before-Check Order Bug
If you store the current prefix sum before checking the complement, the current position can match itself. When k = 0, this counts every single element as a valid "subarray," producing incorrect counts.

Edge Cases to Mention

  • Empty array: Return 0. No elements means no subarrays.
  • All zeros, k = 0: Every subarray sums to 0. For n elements: n*(n+1)/2 subarrays.
  • Single element: Check if nums[0] == k. The {0: 1} base case handles this.
  • k = 0: Still works. The complement is current_sum - 0 = current_sum. Counts how many earlier prefix sums equal the current one.
  • Negative k: Works because the formula is algebraic, not dependent on sign.
Interview Tip
State the Complexity Explicitly
End with: "Time O(n), one pass with O(1) hash map operations. Space O(n), the hash map stores at most n+1 distinct prefix sums." Stating the bound on distinct prefix sums explains why space is O(n) rather than unbounded.
Key Insight
The Complete Solution Arc
  1. Acknowledge brute force O(n²) and why it's slow
  2. Recognize: "subarray sum = difference of prefix sums"
  3. Derive: rearrange to complement lookup
  4. State the base case and why it's needed
  5. Code the solution with check-before-store order
  6. Analyze complexity and edge cases

Common Pitfalls

Forgetting the Base Case

When current_sum == k, the complement is 0. Without {0: 1} in the hash map, the lookup returns 0 and every valid subarray starting at index 0 is silently missed. No error is thrown. The code runs cleanly and returns the wrong answer.

Off-by-One in Range Queries

Confusing indexing conventions causes subtle bugs. The 1-indexed convention (prefix array of length n+1 where prefix[0] = 0) avoids special cases. With this convention, sum(nums[l..r]) = prefix[r+1] - prefix[l]. The 0-indexed convention requires a branch when l = 0.

1# 1-indexed: no special cases
2def range_sum(prefix, l, r):
3 return prefix[r + 1] - prefix[l]
4
5# 0-indexed: requires special handling
6def range_sum_0indexed(prefix, l, r):
7 return prefix[r] if l == 0 else prefix[r] - prefix[l - 1]
Common Mistake
Modifying the Original Array
Computing prefix sums in-place corrupts the input array. If the problem requires the original values later, or within a larger algorithm, this causes bugs. Always use a separate prefix array unless space is critical and the original is definitely not needed.
Common Mistake
Integer Overflow in Java/C++
Prefix sums can grow large. With n = 100,000 elements each valued at 10^9, the prefix sum reaches 10^{14}, exceeding the 32-bit integer range. In Java, use long instead of int. In C++, use long long. Python handles arbitrary precision natively, so this only affects typed languages.
Common Mistake
Using Prefix Sum When Sliding Window Suffices
If the array has only non-negative numbers and the problem asks for "at most k," sliding window is simpler and uses O(1) space. Reaching for a hash map when two pointers would work signals overcomplicated thinking. Check whether the simpler approach works first.
Common Mistake
Not Handling k = 0 Correctly
When k = 0, the complement equals the current prefix sum itself. The store-before-check bug is especially dangerous here because it makes every position match itself, inflating the count. Always verify your solution with k = 0 as a test case.
Common Mistake
Wrong Return Type
Some problems ask for the count of subarrays, others for indices, others for a boolean (exists/doesn't exist). Using the wrong hash map value type (count vs. index vs. existence) produces incorrect results. Read the problem statement twice.

Processing Order in Hash Map Lookups

In the subarray sum template, the complement must be checked before storing the current prefix. Storing first makes the current position count as a valid "earlier" prefix, which is wrong. The algorithm needs prefixes that came before the current position, not including it.

1for num in nums:
2 current_sum += num
3 count += prefix_count.get(current_sum - k, 0) # CHECK first
4 prefix_count[current_sum] = prefix_count.get(current_sum, 0) + 1 # THEN store

Prefix Sum vs Sliding Window

Both handle subarray problems, but they apply under different conditions. The simplest decision rule: if the array can contain negative numbers, sliding window won't work for sum-based problems, so use prefix sum. If all values are non-negative, try sliding window first because the code is simpler and uses less space.

AspectPrefix SumSliding Window
Works with negativesYesNo
"Exactly k" problemsNatural fitNeeds atMost trick
"At most k" problemsHarderNatural fit
Multiple range queriesO(1) eachN/A
SpaceO(n) or O(unique values)O(1) to O(k)

Limitation: Prefix sums work for sums but not for min/max queries, because you cannot "undo" a min with subtraction the way you can undo addition. For range min/max, use a sparse table or segment tree instead.

Sliding window is a greedy algorithm that depends on monotonicity (expanding the window always increases the sum, shrinking always decreases it). Prefix sum is an algebraic technique that works because subtraction undoes addition. When negatives are present, monotonicity breaks and the greedy pointer decisions fail. When all values are non-negative, sliding window is simpler because it avoids the O(n) preprocessing and hash map overhead.

A concrete example demonstrates the failure. For nums = [-1, 1, -1, 1] and target sum = 0, a sliding window starting at index 0 with sum -1+1 = 0 finds one valid subarray. But shrinking the window by removing -1 from the left increases the sum to 1, causing the window to skip past other valid subarrays. The window cannot "unsee" the effect of removing negative elements.

Key Insight
Algebra vs Greedy
Sliding window makes greedy decisions: expand when good, shrink when bad. This requires predictable behavior when moving pointers. Prefix sum uses pure algebra: no decisions, only prefix[j] - prefix[i]. Algebra doesn't care about element signs.

Practice Problems

Practice these in order. Start with range queries, then move to hash map variants.

Basic Range Queries

ProblemDifficultyApproach
Range Sum Query (Immutable)EasyDirect application of Template 1. Build a prefix array once in the constructor, then answer each query in O(1) via subtraction. The simplest prefix sum problem and the right starting point.

Subarray Sum = K Variants

ProblemDifficultyApproach
Subarray Sum Equals KMediumThe canonical prefix sum + hash map problem. The algebraic rearrangement prefix[i] = prefix[j] - k transforms O(n²) brute force into O(n) hash map lookups. The {0: 1} initialization is mandatory for subarrays starting at index 0.
Continuous Subarray SumMediumDivisibility variant with a length >= 2 constraint. Track the first index where each remainder appears. A match is valid only if the current index minus the stored index is at least 2.
Subarray Sums Divisible by KMediumPure divisibility counting via Template 3. Two prefixes with the same remainder mod k produce a divisible subarray sum. Handle negative remainders by adding k when the remainder is negative.

Count Subarrays

ProblemDifficultyApproach
Number of Subarrays with Bounded MaximumMediumReframe as prefix sum of "valid" count at each position. At each index, track how many subarrays ending here have max within bounds.
Count Number of Nice SubarraysMediumReplace each element with 1 (odd) or 0 (even), then apply "subarray sum = k" via Template 2. Alternatively use the atMost(k) - atMost(k-1) decomposition.

Prefix/Suffix Product & Difference Array

ProblemDifficultyApproach
Product of Array Except SelfMediumPrefix and suffix products instead of sums. Build prefix_product from left and suffix_product from right, then multiply them. Avoids division, which would fail on zeros.
Corporate Flight BookingsMediumEach booking is a range update [first, last] += seats. Apply all bookings to the difference array, then reconstruct via prefix sum.
Shifting Letters IIMediumDifference array on character shifts. Each operation shifts a range of letters forward or backward. Accumulate shifts via difference array, then apply the net shift to each character.

Reference Templates

Compact code shapes for quick recovery. Each template includes the context that determines when to use it.

Use when answering multiple range sum queries on a static array.

Template 1: Build + Query
1prefix = [0]
2for num in nums:
3 prefix.append(prefix[-1] + num)
4
5def query(l, r):
6 return prefix[r + 1] - prefix[l]

Use when counting subarrays whose sum equals k. Works with negative numbers.

Template 2: Exact Sum Count
1prefix_count = {0: 1}
2current = 0
3count = 0
4for num in nums:
5 current += num
6 count += prefix_count.get(current - k, 0) # check before store
7 prefix_count[current] = prefix_count.get(current, 0) + 1
8return count

Use when counting subarrays whose sum is divisible by k.

Template 3: Divisibility Count
1remainder_count = {0: 1}
2current = 0
3count = 0
4for num in nums:
5 current += num
6 r = current % k
7 if r < 0:
8 r += k # Java/C++ can return negative; Python % is always non-negative
9 count += remainder_count.get(r, 0)
10 remainder_count[r] = remainder_count.get(r, 0) + 1
11return count

Use when applying many range updates before reading final values. Each update is O(1); reconstruction is one O(n) pass.

Difference Array: Range Update + Reconstruct
1diff = [0] * (n + 1)
2
3def update(l, r, val):
4 diff[l] += val
5 if r + 1 <= n:
6 diff[r + 1] -= val
7
8# Reconstruct final values:
9result, running = [], 0
10for d in diff[:n]:
11 running += d
12 result.append(running)

Pattern Recap

Range queries use O(n) preprocessing followed by O(1) per query via prefix[r+1] - prefix[l]. The leading zero at prefix[0] makes the formula work for every range without special-casing index 0.

Exact sum counting rearranges prefix[j] - prefix[i] = k to prefix[i] = prefix[j] - k, turning an O(n²) search into O(n) hash map lookups. The {0: 1} entry counts the empty prefix, covering subarrays that start at index 0. The check must happen before the store so the current position cannot match itself.

Negative numbers do not break prefix sums. The formula is pure arithmetic: subtraction undoes addition regardless of element sign. Sliding window fails with negatives because its greedy pointer decisions require monotonic sum behavior. Prefix sum avoids that assumption entirely by using a hash map lookup instead.

Difference arrays are the inverse operation. Where prefix sum converts point values into cumulative sums, a difference array converts a range update into two point changes. Applying a prefix sum to the difference array reconstructs the final values. This is useful only when all updates complete before any queries are answered.

Rebuilding the Pattern

When a prefix sum problem feels unclear, start with the question: can the property being asked about be expressed as the difference of two prefix values? For range sum queries the answer is direct. For counting problems, the rearrangement step is the core move.

For counting subarrays, rewrite the target condition as an equation involving two prefix values, then isolate the earlier prefix on one side. The question becomes: how many earlier prefix values satisfy that equation? A hash map answers it in O(1) because the map accumulates every prefix as the scan advances. The {0: 1} base case and check-before-store order both follow from the same constraint: the map must only contain prefixes that came strictly before the current position.

For divisibility, the rearrangement produces a modular condition instead of an exact match. Two prefix sums with the same remainder mod k differ by a multiple of k, so the map stores remainders rather than raw sums. The structure of the loop is identical to Template 2; only what gets stored in the map changes.

For range updates, ask whether all updates can complete before any values are read. If yes, encode each update as two point changes in the difference array and run one prefix sum pass to reconstruct. If updates and queries are interleaved, a segment tree is required instead. The difference array is the right choice when batch processing is possible.

Check Your Understanding

Use these questions to check whether you can reason through the pattern without looking at the template.

Ready to test it?

10 questions covering prefix sum patterns and difference arrays.

Practice this pattern

Apply the guide to complete interview problems with explanations and code.

Learn Prefix Sum in a guided sequence

The Interview Course connects this pattern to its prerequisites, worked lessons, and progressively harder problems.

Explore the course