2Brute2Best
🔥0

Subarray Sums Divisible by K

Medium·LeetCode #974Basic Anchor
LeetCode
30-Second Mental ModelPrefix Sum (Modulo / divisibility) — count equal remainders
Complexity:O(N²)→O(N + K)
Key Insight

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.

Core Validity Invariant

nums[l..r] is divisible by k exactly when prefix[l] and prefix[r+1] leave the same remainder mod k.

01

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, …).

Input

An integer array nums and an integer k (k ≥ 2).

Output

The number of non-empty subarrays whose sum is a multiple of k.

Example 1
Input: nums = [4, 5, 0, -2, -3, 1], k = 5
Output: 7

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).

Example 2
Input: nums = [5], k = 9
Output: 0

The only subarray is [5], and 5 is not a multiple of 9.

Constraints
  • • 1 ≤ nums.length ≤ 3 · 10^4
  • • −10^4 ≤ nums[i] ≤ 10^4
  • • 2 ≤ k ≤ 10^4
02

First, think yourself

Before opening any solution, try to answer these on paper. Use hints only when stuck — each hint is counted.

  1. 1.

    Any subarray sum is the difference of two running totals. Which two?

  2. 2.

    When is the difference of two numbers a multiple of k? Do the numbers need to be equal?

  3. 3.

    If I only care about divisibility, do I need the full running total, or is a smaller piece of it enough?

  4. 4.

    How many different values can that smaller piece take? Does that suggest an array instead of a hash map?

  5. 5.

    What does C++ give for -7 % 5? Is that what I want?

03

Brute force approach

05

Optimized approach

06

Why does this algorithm work?

07

Dry run

08

All approaches compared

ApproachIdeaTimeSpace
Brute ForceFix each start, grow the end, test sum % k == 0O(N²)O(1)
OptimalEqual prefix remainders ⇔ divisible subarray; count each remainderO(N + K)O(K)
09

Why does this belong to this pattern?

Clues in the problem statement
  • “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.
Contiguous range+count+sum divisible by k+negatives allowed→Prefix Sum (Modulo / divisibility) — count equal remainders

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.

10

Don't make this mistake

Using C++ % on a negative running total
✗ rem = (rem + x) % k; // can be negative
✓ rem = ((rem + x) % k + k) % k;

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.

Forgetting the empty prefix
✗ vector<int> cnt(k, 0);
✓ vector<int> cnt(k, 0); cnt[0] = 1;

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.

Adding the current prefix before looking it up
✗ cnt[rem]++; count += cnt[rem];
✓ count += cnt[rem]; cnt[rem]++;

Counting first would pair the prefix with itself, which is an empty subarray. Every index would add one extra.

Storing sums and looking up prefix − k
✗ count += seen[prefix - k]; seen[prefix]++;
✓ count += cnt[rem]; cnt[rem]++;

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.

11

Want to implement this?

Write it yourself on LeetCode, then come back and compare your code with the solutions above.

Open coding workspace
How did it go?
Beyond The LeetCode Problem

How this concept connects to System Design & C++ Systems

Bridge DSA with production engineering

Top engineering interviews test whether you understand how algorithms like Prefix Sum translate into production architectures and low-level memory mechanics.