Subarray Sums Divisible by K
A subarray sum is prefix[r+1] − prefix[l]. It is divisible by k exactly when the two prefixes have the same remainder mod k. So walk once, keep the current remainder, and keep cnt[r] = how many earlier prefixes had remainder r. At each index, add cnt[rem] to the answer.
nums[l..r] is divisible by k exactly when prefix[l] and prefix[r+1] leave the same remainder mod k.
Problem
You are given an integer array nums (it may contain negative numbers) and a number k.
Count how many contiguous, non-empty subarrays have a sum that is divisible by k (the sum is 0, k, −k, 2k, …).
An integer array nums and an integer k (k ≥ 2).
The number of non-empty subarrays whose sum is a multiple of k.
The 7 subarrays are [5], [5,0], [0], [-2,-3], [0,-2,-3], [5,0,-2,-3] and the whole array [4,5,0,-2,-3,1] (sum 5).
The only subarray is [5], and 5 is not a multiple of 9.
- • 1 ≤ nums.length ≤ 3 · 10^4
- • −10^4 ≤ nums[i] ≤ 10^4
- • 2 ≤ k ≤ 10^4
First, think yourself
Before opening any solution, try to answer these on paper. Use hints only when stuck — each hint is counted.
- 1.
Any subarray sum is the difference of two running totals. Which two?
- 2.
When is the difference of two numbers a multiple of k? Do the numbers need to be equal?
- 3.
If I only care about divisibility, do I need the full running total, or is a smaller piece of it enough?
- 4.
How many different values can that smaller piece take? Does that suggest an array instead of a hash map?
- 5.
What does C++ give for
-7 % 5? Is that what I want?
Brute force approach
Optimized approach
Why does this algorithm work?
Dry run
All approaches compared
| Approach | Idea | Time | Space |
|---|---|---|---|
| Brute Force | Fix each start, grow the end, test sum % k == 0 | O(N²) | O(1) |
| Optimal | Equal prefix remainders ⇔ divisible subarray; count each remainder | O(N + K) | O(K) |
Why does this belong to this pattern?
- “contiguous subarrays”Ranges [l..r] → a range sum is a difference of two prefix sums.
- “count”Count pairs of prefixes, so store how many times each key appeared, not just whether it did.
- “divisible by k”The key is the prefix **remainder**, not the prefix value. Equal remainders ⇔ divisible difference.
- “may be negative”A window will not work, and C++ `%` can be negative → normalise the remainder.
This is the anchor of the Modulo sub-pattern. It is Subarray Sum Equals K with one change: the map key is prefix % k instead of prefix, and the lookup is the same remainder instead of prefix − k. Continuous Subarray Sum keeps the same remainder idea but only asks whether a long-enough pair exists. Make Sum Divisible by P looks for a different remainder (cur − need) and wants the shortest pair.
Don't make this mistake
In C++, -1 % 3 is -1, but the prefix −1 belongs with remainder 2 (−1 = 3·(−1) + 2). Prefixes −1 and 2 differ by 3, so they should match, but keys -1 and 2 never do. With a vector it is even worse: cnt[-1] reads outside the array.
A subarray that starts at index 0 pairs with the empty prefix (sum 0). Without it, [5] with k = 5 gives 0 instead of 1.
Counting first would pair the prefix with itself, which is an empty subarray. Every index would add one extra.
This is a pattern-confusion trap. Looking up prefix − k counts subarrays whose sum equals k (Subarray Sum Equals K). Here the sum can be 0, k, 2k, −k, … — any multiple. Store remainders and look up the same remainder.
Want to implement this?
Write it yourself on LeetCode, then come back and compare your code with the solutions above.
Open coding workspaceHow this concept connects to System Design & C++ Systems
Top engineering interviews test whether you understand how algorithms like Prefix Sum translate into production architectures and low-level memory mechanics.
Foundations: Object-Oriented Principles in C++23
Class invariants, pure virtual interfaces, and RAII smart pointers.
Food Delivery System (Zomato Architecture)
Real-world machine coding application of algorithms and design patterns.
Pointers vs References Deep Dive
Pointer arithmetic and memory layout under the hood.
Virtual Method Table (vtable) & Memory Layout
Polymorphism, vptr overhead, and multiple inheritance in memory.