Prefix Sum — The Complete Guide
Master the Prefix Sum pattern from first principles. Learn to recognize it instantly, evolve brute-force into optimal solutions, and confidently solve interview questions.
Table of Contents
Intuition from Scratch
Why does this pattern exist?
Imagine you're a teacher and you have a list of daily test scores for a student over the entire year. A parent asks: "What was the average score between March 10 and April 20?" You could add up every score in that range from scratch. But what if they ask again for a different range? And again? Each time you'd re-add dozens of numbers.
Instead, you could keep a running total — after day 1 the total is 85, after day 2 it's 170, after day 3 it's 248, and so on. Now to find the sum between any two days, you just subtract two numbers: sum(i, j) = runningTotal[j] − runningTotal[i − 1]. One subtraction instead of adding up the entire range. That running total is a prefix sum.
The pattern exists because many problems ask about range sums or subarray sums. Computing them from scratch each time is O(n) per query. Precomputing a prefix sum array makes each query O(1) — you trade O(n) space for unlimited O(1) queries.
Analogies to Build Intuition
The Bank Balance
Your bank balance is a prefix sum of all your transactions. To find how much you spent between Tuesday and Friday, you don't re-add every transaction — you just subtract Tuesday's balance from Friday's balance. One subtraction, instant answer.
The Odometer
A car's odometer tracks total distance driven. To find how far you drove between two gas stations, subtract the odometer reading at the first station from the reading at the second. The odometer IS a prefix sum of all the small distances you've driven.
The Cumulative Frequency Table
In statistics, a cumulative frequency table stores running totals. To find how many values fall in a range, subtract two cumulative frequencies. Same idea — precompute once, query instantly.
What kind of problems does it solve?
Prefix Sum is your go-to when:
- You need the sum of a subarray from index i to j
- You need to answer multiple range sum queries on the same array
- You need to count subarrays with a specific sum
- You need prefix/suffix products (Product of Array Except Self)
- You need to check if a subarray sum is divisible by k
The core insight
Prefix Sum converts range sum queries from O(n) to O(1) by precomputing cumulative totals. The formula is simple: sum(i, j) = prefix[j + 1] − prefix[i]. Build once in O(n), query unlimited times in O(1).
Pattern Recognition
Recognizing Prefix Sum in the wild is the most important skill. Here are the signals to look for and the traps to avoid.
Keywords & Signals in the Problem Statement
Use Prefix Sum when you see...
- ✅"Sum of elements between index i and j"
- ✅"Subarray sum equals k"
- ✅"Number of subarrays with sum divisible by k"
- ✅"Product of array except self"
- ✅"Multiple range sum queries on a static array"
- ✅"Contiguous subarray sum" + counting or checking
- ✅"Running total" or "cumulative" in the description
- ✅"Equilibrium index" or "pivot index"
- ✅"Can you split the array into equal-sum parts?"
Don't use Prefix Sum when...
- ❌The array is modified between queries — use Segment Tree or BIT
- ❌You need the actual subarray, not just the sum — Sliding Window may be better
- ❌The problem is about pairs or ordering — Two Pointers or Sorting fits better
- ❌You only need a single range sum — just iterate, no precomputation needed
- ❌The problem involves max/min subarray — use Kadane's algorithm
- ❌You need contiguous window optimization — use Sliding Window
Prefix Sum vs. Similar Patterns
| Pattern | When to Use | Key Difference |
|---|---|---|
| Prefix Sum | Range sum queries, subarray sum counting | Precomputes cumulative sums; answers range queries in O(1) |
| Sliding Window | Longest/shortest contiguous subarray with constraint | Maintains a dynamic window; doesn't precompute anything |
| Difference Array | Multiple range updates (not queries) | Inverse of prefix sum — efficient range updates, not queries |
| Segment Tree | Range queries + range updates on mutable arrays | Handles both queries and updates in O(log n); more complex |
The prefix sum + hash map combo
When you see "count subarrays with sum equal to k," it's almost always prefix sum + hash map. The hash map stores how many times each prefix sum has occurred. For each new prefix sum, check if (currentSum − k) exists in the map. This is the single most important Prefix Sum technique for interviews.
Brute Force → Optimized Thinking
Let's walk through the thinking evolution using two concrete examples.
Example 1: Range Sum Query
Given an array and multiple queries [i, j], return the sum of elements from index i to j.
Brute Force — Recompute Each Query
For each query, loop from i to j and add up the elements. If you have q queries, total time is O(n × q). For large arrays with many queries, this is too slow.
function rangeSumBrute(nums: number[], i: number, j: number): number { let sum = 0; for (let idx = i; idx <= j; idx++) { sum += nums[idx]; } return sum; } // O(n) per query. With q queries: O(n × q) total.
Optimal — Precompute Prefix Sums
Build a prefix sum array once. Then any range sum is just one subtraction: prefix[j+1] − prefix[i]. The prefix array stores the cumulative sum up to each index.
function buildPrefix(nums: number[]): number[] { const prefix = new Array(nums.length + 1).fill(0); for (let i = 0; i < nums.length; i++) { prefix[i + 1] = prefix[i] + nums[i]; } return prefix; } function rangeSum(prefix: number[], i: number, j: number): number { return prefix[j + 1] - prefix[i]; } // Build once: O(n). Each query: O(1). Total for q queries: O(n + q). // prefix[0] = 0 (empty prefix — this avoids edge cases with i = 0)
Example 2: Subarray Sum Equals K
Count the number of contiguous subarrays whose sum equals k.
Brute Force — Check Every Subarray
For each starting index, extend the subarray and check if the running sum equals k. This checks all n² subarrays.
function subarraySumBrute(nums: number[], k: number): number { let count = 0; for (let i = 0; i < nums.length; i++) { let sum = 0; for (let j = i; j < nums.length; j++) { sum += nums[j]; if (sum === k) count++; } } return count; } // O(n²) — checks every possible subarray.
Optimal — Prefix Sum + Hash Map
Key insight: if prefix[j] − prefix[i] = k, then the subarray (i, j] sums to k. So for each prefix sum, we check how many earlier prefix sums equal (currentSum − k). A hash map counts prefix sum occurrences.
function subarraySum(nums: number[], k: number): number { const prefixCount = new Map<number, number>(); prefixCount.set(0, 1); // empty prefix (handles subarrays starting at index 0) let sum = 0; let count = 0; for (const num of nums) { sum += num; const target = sum - k; if (prefixCount.has(target)) { count += prefixCount.get(target)!; } prefixCount.set(sum, (prefixCount.get(sum) ?? 0) + 1); } return count; } // O(n) time, O(n) space. Each element processed once. // The map stores: "how many times has this prefix sum appeared before?" // If (sum - k) appeared before, those are valid subarray start points.
Why Does the Optimization Work?
Range sums reduce to subtraction
sum(i, j) = prefix[j+1] − prefix[i]. This is the fundamental identity. Any range sum is the difference of two prefix sums.
Hash map turns 'find a pair' into O(1)
'Find two prefix sums whose difference is k' is like Two Sum — for each prefix sum, check if (sum − k) exists in the map. The map gives O(1) lookup.
The general principle
Prefix Sum precomputes cumulative information so that range queries become constant-time lookups. Combined with a hash map, it can count subarrays matching a sum condition in O(n).
The thinking process
In interviews, show this progression: "The brute force checks all subarrays in O(n²). But I notice that subarray sums can be expressed as differences of prefix sums. So I can use a hash map to find matching prefix sums in O(1), giving me O(n) total."
Core Templates
Prefix Sum has three main templates. Memorize these — most problems are variations of one of them.
Template 1: Basic Prefix Sum Array
Build a prefix array for O(1) range sum queries. The prefix array has length n+1 where prefix[0] = 0 to handle edge cases cleanly.
// Build prefix sum array (length n + 1) const prefix = new Array(nums.length + 1).fill(0); for (let i = 0; i < nums.length; i++) { prefix[i + 1] = prefix[i] + nums[i]; } // Query: sum of nums[i..j] (inclusive) function rangeSum(i: number, j: number): number { return prefix[j + 1] - prefix[i]; } // Why prefix[0] = 0? // So that rangeSum(0, j) = prefix[j+1] - prefix[0] = prefix[j+1] // Without it, you'd need a special case for i = 0.
Template 2: Prefix Sum + Hash Map (Count Subarrays)
Count subarrays whose sum equals k. The hash map tracks how many times each prefix sum has occurred.
function countSubarrays(nums: number[], k: number): number { const prefixCount = new Map<number, number>(); prefixCount.set(0, 1); // empty prefix — critical! let sum = 0; let count = 0; for (const num of nums) { sum += num; // running prefix sum count += prefixCount.get(sum - k) ?? 0; // how many valid starts? prefixCount.set(sum, (prefixCount.get(sum) ?? 0) + 1); } return count; } // Why set(0, 1) initially? // If sum itself equals k, then (sum - k) = 0. // We need the map to report that the "empty prefix" exists once. // Without it, we'd miss subarrays starting at index 0.
Template 3: Prefix + Suffix (Product of Array Except Self)
When you need the product (or sum) of all elements except the current one, compute prefix products from the left and suffix products from the right.
function productExceptSelf(nums: number[]): number[] { const n = nums.length; const result = new Array(n).fill(1); // Left pass: result[i] = product of all elements to the LEFT of i let leftProduct = 1; for (let i = 0; i < n; i++) { result[i] = leftProduct; leftProduct *= nums[i]; } // Right pass: multiply by product of all elements to the RIGHT of i let rightProduct = 1; for (let i = n - 1; i >= 0; i--) { result[i] *= rightProduct; rightProduct *= nums[i]; } return result; } // Time: O(n) — two passes // Space: O(1) — result array doesn't count as extra space // result[i] = leftProduct[i] × rightProduct[i]
Which template to pick?
Ask yourself: (1) Do I need range sum queries? → Template 1. (2) Do I need to count subarrays with a specific sum? → Template 2. (3) Do I need the aggregate of everything except the current element? → Template 3.
Variations & Sub-Patterns
Prefix Sum isn't just about sums — the concept generalizes to any associative operation. Here are the most common variations.
| Variation | What It Precomputes | Classic Example |
|---|---|---|
| Basic range sum | Cumulative sum → O(1) range queries | Range Sum Query - Immutable |
| Prefix sum + hash map | Running sum + count of each prefix sum | Subarray Sum Equals K |
| Prefix/suffix product | Left products and right products | Product of Array Except Self |
| Prefix XOR | Cumulative XOR for range XOR queries | XOR Queries of a Subarray |
| 2D prefix sum | Cumulative sum over a matrix | Range Sum Query 2D - Immutable |
| Prefix sum modulo k | Running sum mod k + pigeonhole principle | Continuous Subarray Sum, Subarray Sums Divisible by K |
| Prefix count / frequency | Running count of specific elements | Number of Good Pairs, Pivot Index |
Problems mentioned above
Deep Dive: Prefix Sum Modulo K
When the problem asks about subarrays whose sum is divisible by k, use prefix sums modulo k. The key insight: if two prefix sums have the same remainder when divided by k, the subarray between them has a sum divisible by k.
function subarraysDivByK(nums: number[], k: number): number { const modCount = new Map<number, number>(); modCount.set(0, 1); // empty prefix has remainder 0 let sum = 0; let count = 0; for (const num of nums) { sum += num; // Handle negative remainders: ((sum % k) + k) % k const mod = ((sum % k) + k) % k; count += modCount.get(mod) ?? 0; modCount.set(mod, (modCount.get(mod) ?? 0) + 1); } return count; } // If prefix[i] % k === prefix[j] % k, then sum(i+1, j) is divisible by k. // The map counts how many prefix sums share each remainder.
Deep Dive: 2D Prefix Sum
For matrix range sum queries, build a 2D prefix sum where each cell stores the sum of the rectangle from (0,0) to (r,c). Use inclusion-exclusion to query any sub-rectangle in O(1).
// Build 2D prefix sum const m = matrix.length, n = matrix[0].length; const prefix = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0)); for (let r = 0; r < m; r++) { for (let c = 0; c < n; c++) { prefix[r + 1][c + 1] = matrix[r][c] + prefix[r][c + 1] + prefix[r + 1][c] - prefix[r][c]; // subtract double-counted overlap } } // Query: sum of rectangle from (r1, c1) to (r2, c2) function regionSum(r1: number, c1: number, r2: number, c2: number): number { return ( prefix[r2 + 1][c2 + 1] - prefix[r1][c2 + 1] - prefix[r2 + 1][c1] + prefix[r1][c1] // add back double-subtracted corner ); } // Build: O(m × n). Each query: O(1).
Visual Walkthrough
Let's trace through two examples step by step.
Example 1: Building a Prefix Sum Array
Array: [3, 1, 4, 1, 5, 9, 2, 6] Index: 0 1 2 3 4 5 6 7 Building prefix (length 9, prefix[0] = 0): prefix[0] = 0 prefix[1] = 0 + 3 = 3 prefix[2] = 3 + 1 = 4 prefix[3] = 4 + 4 = 8 prefix[4] = 8 + 1 = 9 prefix[5] = 9 + 5 = 14 prefix[6] = 14 + 9 = 23 prefix[7] = 23 + 2 = 25 prefix[8] = 25 + 6 = 31 Prefix: [0, 3, 4, 8, 9, 14, 23, 25, 31] Query: sum(2, 5) = sum of [4, 1, 5, 9] = prefix[6] - prefix[2] = 23 - 4 = 19 ✓ (4 + 1 + 5 + 9 = 19) Query: sum(0, 3) = sum of [3, 1, 4, 1] = prefix[4] - prefix[0] = 9 - 0 = 9 ✓ (3 + 1 + 4 + 1 = 9) Query: sum(7, 7) = sum of [6] = prefix[8] - prefix[7] = 31 - 25 = 6 ✓
Example 2: Subarray Sum Equals K (Prefix Sum + Hash Map)
Array: [1, 2, 1, -1, 1, 2] k = 3 Map starts: {0: 1} (empty prefix) sum = 0, count = 0 Step 1: num=1, sum=1 target = 1 - 3 = -2 → not in map map = {0:1, 1:1} count=0 Step 2: num=2, sum=3 target = 3 - 3 = 0 → map has 0 with count 1 → count += 1 map = {0:1, 1:1, 3:1} count=1 Found: subarray [1,2] sums to 3 ★ Step 3: num=1, sum=4 target = 4 - 3 = 1 → map has 1 with count 1 → count += 1 map = {0:1, 1:1, 3:1, 4:1} count=2 Found: subarray [2,1] sums to 3 ★ Step 4: num=-1, sum=3 target = 3 - 3 = 0 → map has 0 with count 1 → count += 1 map = {0:1, 1:1, 3:2, 4:1} count=3 Found: subarray [1,2,1,-1] sums to 3 ★ Step 5: num=1, sum=4 target = 4 - 3 = 1 → map has 1 with count 1 → count += 1 map = {0:1, 1:1, 3:2, 4:2} count=4 Found: subarray [2,1,-1,1] sums to 3 ★ Step 6: num=2, sum=6 target = 6 - 3 = 3 → map has 3 with count 2 → count += 2 map = {0:1, 1:1, 3:2, 4:2, 6:1} count=6 Found: 2 subarrays ending here sum to 3 ★★ Answer: 6 Key insight: the map tells us "how many earlier prefix sums would make the current subarray sum equal to k?" Each such prefix sum is a valid starting point for a subarray ending at the current index.
Practice Problems
These 7 problems are carefully ordered from easy to hard. Each one teaches a different aspect of Prefix Sum. Solve them in order.
LC 303. Range Sum Query - Immutable
Why this pattern:
The textbook prefix sum problem. Build a prefix array in the constructor, answer each sumRange(i, j) query as prefix[j+1] − prefix[i] in O(1).
Key idea: Precompute prefix[i] = nums[0] + ... + nums[i-1]. Then sum(i, j) = prefix[j+1] − prefix[i]. Construction is O(n), each query is O(1).
LC 724. Find Pivot Index
Why this pattern:
Prefix sum to compute left sum and right sum at each index. The pivot is where leftSum === rightSum. Use total sum − leftSum − nums[i] for rightSum.
Key idea: Compute total sum. Iterate left to right, maintaining leftSum. At each index, rightSum = total − leftSum − nums[i]. If leftSum === rightSum, return i.
LC 560. Subarray Sum Equals K
Why this pattern:
Prefix sum + hash map. For each running sum, check if (sum − k) was seen before. The count of such occurrences gives the number of valid subarrays ending here.
Key idea: If prefix[j] − prefix[i] = k, then subarray (i, j] sums to k. Use a map to count prefix sums seen so far and look up (currentSum − k) at each step.
LC 238. Product of Array Except Self
Why this pattern:
Prefix and suffix products. Build a left-product pass and a right-product pass, then multiply them element-wise. No division needed.
Key idea: result[i] = (product of all elements left of i) × (product of all elements right of i). Forward pass computes left products, backward pass multiplies in right products.
LC 523. Continuous Subarray Sum
Why this pattern:
Prefix sum modulo k + hash map. If two prefix sums have the same remainder mod k, the subarray between them is divisible by k. Store the earliest index for each remainder.
Key idea: Track prefix sum mod k. If the same remainder appears at indices i and j where j − i ≥ 2, the subarray sums to a multiple of k. Store first occurrence of each remainder.
LC 974. Subarray Sums Divisible by K
Why this pattern:
Prefix sum modulo k + counting. Count pairs of prefix sums with the same remainder. Handle negative remainders with ((sum % k) + k) % k.
Key idea: If prefix[i] % k === prefix[j] % k, then sum(i+1, j) is divisible by k. Count how many prefix sums share each remainder. For each group of size c, add c to the answer.
LC 304. Range Sum Query 2D - Immutable
Why this pattern:
2D prefix sum with inclusion-exclusion. Build a 2D prefix matrix, then query any sub-rectangle in O(1) using the inclusion-exclusion formula.
Key idea: prefix[r][c] = sum of rectangle (0,0) to (r-1,c-1). Query (r1,c1)→(r2,c2) = prefix[r2+1][c2+1] − prefix[r1][c2+1] − prefix[r2+1][c1] + prefix[r1][c1].
Practice strategy
For each problem: (1) Identify which template applies (basic, hash map, or prefix+suffix). (2) Write the prefix sum formula on paper first. (3) Handle the edge case of prefix[0] = 0. (4) After solving, write: "This uses [template] because [signal]."
Common Mistakes
These are the traps that catch people on Prefix Sum problems. Learn them here so you don't learn them in an interview.
Forgetting prefix[0] = 0
You build the prefix array starting at index 0 instead of having a leading zero. This breaks rangeSum(0, j) because there's no prefix[-1] to subtract.
✅Always create prefix with length n+1 and set prefix[0] = 0. Then prefix[i+1] = prefix[i] + nums[i]. This handles all ranges uniformly, including those starting at index 0.
Forgetting to initialize the hash map with (0, 1)
In the prefix sum + hash map pattern, you forget to seed the map with prefixCount.set(0, 1). This causes you to miss subarrays that start at index 0.
✅Always initialize with prefixCount.set(0, 1). This represents the 'empty prefix' — if the running sum itself equals k, then (sum − k) = 0 needs to be found in the map.
Off-by-one in the range formula
Using prefix[j] − prefix[i] instead of prefix[j+1] − prefix[i], or confusing inclusive vs exclusive bounds. The answer is off by one element.
✅Convention: prefix[i] = sum of nums[0..i-1]. Then sum(i, j) inclusive = prefix[j+1] − prefix[i]. Write this formula as a comment in your code and verify with a 1-element example.
Not handling negative remainders in modulo problems
In JavaScript/TypeScript, -7 % 3 = -1 (not 2). This gives wrong hash map keys for prefix sum modulo k problems with negative numbers.
✅Always normalize: const mod = ((sum % k) + k) % k. This ensures the remainder is always in [0, k-1] regardless of the sign of sum.
Using prefix sum when the array is mutable
You precompute prefix sums but then the array gets updated. The prefix array is now stale and all queries return wrong answers.
✅Prefix sum only works on static (immutable) arrays. If the array changes between queries, use a Segment Tree or Binary Indexed Tree (Fenwick Tree) instead.
Confusing prefix sum with sliding window
You see 'subarray sum' and reach for prefix sum, but the problem asks for the longest/shortest subarray with sum ≥ target. Prefix sum + hash map counts exact sums, not inequality constraints.
✅For 'subarray sum equals k' → prefix sum + hash map. For 'longest/shortest subarray with sum ≥ target' (positive numbers) → sliding window. For negative numbers + inequality → prefix sum + monotonic deque.
Interview Strategy
Knowing the pattern is half the battle. Here's how to present it in an interview setting to maximize your score.
The 5-Step Interview Flow
Clarify & Confirm
'The array is static (no updates between queries)? I need the sum between indices i and j? Can there be negative numbers?' — Confirming immutability is key for prefix sum.
State the Brute Force
'The brute force recomputes the sum for each query in O(n). With q queries that's O(n × q). But I can precompute prefix sums to make each query O(1).' — Name the optimization.
Explain the Key Formula
'I'll build a prefix array where prefix[i] = sum of the first i elements. Then sum(i, j) = prefix[j+1] − prefix[i]. This is O(n) to build and O(1) per query.' — Write the formula explicitly.
Code It Clean
Write the solution. Use clear variable names: prefix, sum, count. Add a comment for the formula. For the hash map variant, explain why you initialize with (0, 1).
Test & Analyze
Trace through a small example: 'For [1, 2, 3, 4], prefix = [0, 1, 3, 6, 10]. sum(1, 3) = prefix[4] − prefix[1] = 10 − 1 = 9. Correct: 2+3+4=9.' State complexity: O(n) build, O(1) query.
Common Follow-Up Questions
| Follow-Up | How to Handle |
|---|---|
| "What if the array gets updated?" | Prefix sum breaks. Switch to Segment Tree (O(log n) update + query) or Binary Indexed Tree (Fenwick Tree). |
| "Can you do it without extra space?" | For Product Except Self, yes — use the output array for left products, then a single variable for right products. For range queries, you need the prefix array. |
| "What about 2D range queries?" | Build a 2D prefix sum matrix. Use inclusion-exclusion for O(1) sub-rectangle queries. Build time is O(m × n). |
| "What if there are negative numbers?" | Prefix sum + hash map still works for exact sum queries. But sliding window breaks with negatives — mention this distinction. |
| "Can you count subarrays with sum in a range [lo, hi]?" | Count subarrays with sum ≤ hi minus count with sum ≤ lo-1. Each count uses prefix sum + sorted structure or hash map. |
| "What about XOR instead of sum?" | Same idea — prefix XOR. XOR is its own inverse, so range XOR = prefix[j+1] XOR prefix[i]. No subtraction needed. |
The meta-skill
Interviewers love Prefix Sum because it tests whether you can (1) recognize the precomputation opportunity, (2) write the formula correctly with the right indices, and (3) handle the hash map variant for counting problems. Practice explaining the formula out loud — "prefix[j+1] minus prefix[i]" should roll off your tongue.
Summary & Cheat Sheet
Pin this section. Review it before mock interviews and contests.
Quick Revision Cheat Sheet
Core idea: Precompute cumulative sums so any range sum becomes O(1): sum(i,j) = prefix[j+1] − prefix[i]
Build time: O(n) to build the prefix array. O(1) per query after that.
prefix[0] = 0: Always include a leading zero. It handles ranges starting at index 0 without special cases.
Hash map combo: For 'count subarrays with sum = k': store prefix sum counts in a map. Check if (sum − k) exists.
Initialize map: Always seed with prefixCount.set(0, 1) — the empty prefix. Missing this loses subarrays starting at index 0.
Prefix + suffix: For 'product except self': left pass for prefix products, right pass for suffix products. O(n) time, O(1) extra space.
Modulo variant: For 'divisible by k': same remainder mod k means the subarray between is divisible. Normalize negatives: ((sum % k) + k) % k.
2D extension: Build 2D prefix matrix. Query sub-rectangles with inclusion-exclusion in O(1).
Static only: Prefix sum works on immutable arrays. For mutable arrays, use Segment Tree or BIT.
Mental trigger: "Range sum query" or "count subarrays with sum = k" → Prefix Sum
Decision Flowchart
Is the problem about RANGE SUMS or SUBARRAY SUMS? ├── NO → Not Prefix Sum. Consider Sliding Window, Two Pointers, or DP. └── YES → Is the array static (no updates between queries)? ├── NO → Use Segment Tree or Binary Indexed Tree instead. └── YES → What do you need? ├── Range sum queries → Template 1: Basic prefix array ├── Count subarrays with sum = k → Template 2: Prefix + hash map ├── Product except self → Template 3: Prefix + suffix ├── Divisible by k → Prefix sum modulo k + hash map └── 2D range queries → 2D prefix sum + inclusion-exclusion What's the key formula? ├── sum(i, j) = prefix[j + 1] − prefix[i] ├── subarray sum = k ↔ prefix[j] − prefix[i] = k ↔ prefix[i] = prefix[j] − k └── divisible by k ↔ prefix[i] % k === prefix[j] % k
One last thing
Prefix Sum is one of the most elegant patterns in DSA. The idea is simple — precompute running totals — but it unlocks O(1) range queries and O(n) subarray counting that would otherwise be O(n²). The prefix sum + hash map combo alone solves a huge family of interview problems. Master the three templates in Section 04, and you'll recognize this pattern instantly.