AlgoMaster Logo

Continuous Subarray Sum

Last Updated: March 21, 2026

medium
4 min read

Understanding the Problem

We need to find a contiguous subarray of length at least 2 whose sum is divisible by k. The key phrase here is "multiple of k," which means the sum could be 0, k, 2k, 3k, and so on. We're not looking for an exact target. We're looking for any sum that leaves a remainder of 0 when divided by k.

This is different from "Subarray Sum Equals K" (LeetCode 560) where we have a fixed target. Here, the target is any value in the infinite set {0, k, 2k, 3k, ...}. Checking against every possible multiple would be impractical, which is the hint that we need a smarter mathematical approach.

The minimum length requirement of 2 is easy to overlook but matters. A single element that's a multiple of k doesn't count. This constraint will influence how we validate our answer in every approach.

Key Constraints:

  • nums.length up to 10^5 means O(n^2) will be tight (around 10^10 operations in the worst case), so we should aim for O(n)
  • nums[i] can be 0, which means consecutive zeros form a valid subarray (sum = 0, which is a multiple of any k)
  • k can be very large (up to 2^31 - 1), so we can't create an array indexed by k
  • The sum can reach 2^31 - 1, so we need to be mindful of integer overflow in some languages

Approach 1: Brute Force

Intuition

The most straightforward approach is to check every possible subarray of length 2 or more, compute its sum, and see if it's divisible by k. We fix a starting index, extend the subarray one element at a time, and check the running sum at each step.

Since we're building the sum incrementally (adding one element at a time as we extend the end pointer), we avoid recomputing the sum from scratch for each subarray. This brings us from O(n^3) down to O(n^2).

Algorithm

  1. For each starting index i from 0 to n - 2
  2. Maintain a running sum starting from nums[i]
  3. For each ending index j from i + 1 to n - 1, add nums[j] to the running sum
  4. If the running sum is divisible by k, return true
  5. If no valid subarray is found after checking all pairs, return false

Example Walkthrough

1Initialize: check every subarray of length >= 2, k=6
0
23
i
1
j
2
2
4
3
6
4
7
1/6

Code

The bottleneck here is the nested loop: for each starting index, we scan every possible ending index. With n up to 10^5, that's up to 5 billion operations. What if we could process each element exactly once and use a mathematical property to detect valid subarrays in O(1) per element?

Approach 2: Prefix Sum + Hash Map (Optimal)

Intuition

Here's the key mathematical insight. If we define prefixSum[j] as the sum of the first j elements, then the sum of any subarray from index i+1 to j is prefixSum[j] - prefixSum[i].

We want this subarray sum to be a multiple of k:

prefixSum[j] - prefixSum[i] ≡ 0 (mod k)

Which means:

prefixSum[j] % k == prefixSum[i] % k

So two prefix sums with the same remainder when divided by k define a subarray whose sum is a multiple of k. This transforms the problem into: find two prefix sums with the same remainder that are at least 2 positions apart.

Now the approach becomes clear. As we compute prefix sums, we store the remainder prefixSum % k in a hash map. If we see the same remainder again and the indices are at least 2 apart, we've found a valid subarray. We store the earliest index for each remainder because we only need to know if a valid pair exists, not find all of them.

One subtle detail: we initialize the map with {0: -1}. This handles the case where the prefix sum itself is a multiple of k (i.e., a subarray starting from index 0). Without this initialization, we'd miss subarrays like [6, 0] with k = 6, where the prefix sum at index 1 is already 6.

Algorithm

  1. Create a hash map and initialize it with {0: -1} (remainder 0 seen at virtual index -1)
  2. Maintain a running prefix sum as we iterate through the array
  3. For each index i, compute prefixSum % k
  4. If this remainder already exists in the map and i - map[remainder] >= 2, return true
  5. If the remainder is not in the map, store it with the current index (we only store the first occurrence to maximize the subarray length)
  6. If we finish without finding a match, return false

Code