Continuous Subarray Sum
Problem
nums and a positive integer
k, decide whether some contiguous subarray of length at least 2 has a sum
that is a multiple of k (zero counts as a multiple). Return
true or false.nums = [23,2,4,6,7], k = 6Outputtrue ([2,4] sums to 6)nums = [23,2,6,4,7], k = 6Outputtrue (whole array sums to 42)nums = [23,2,6,4,7], k = 13Outputfalse1 ≤ n ≤ 105 · 0 ≤ nums[i] ≤ 109 · total sum ≤ 231-1 · 1 ≤ k ≤ 231-1
Try it yourself
class Solution {
public boolean checkSubarraySum(int[] nums, int k) {
}
}
(i, j] sums to a multiple of k exactly
when the prefix sums at i and j leave the same remainder
mod k. Remember the earliest index of each remainder and check that the
gap is at least 2.Instinct Repeated range-sum queries, or "count subarrays whose sum equals / divides K" with negatives allowed? Store running totals and subtract two of them.
Memory hack Any range is two prefixes apart - sum(i..j) = P[j+1] - P[i], so remember every P you've passed.
Open the Prefix Sum guide →Approaches
Brute ForceAll Subarrays with Running Sum
O(n2)Space O(1)Fix a start index and extend the end, maintaining the running sum. As soon as
a window of length ≥ 2 has sum % k == 0, we are done. A running
sum avoids re-adding the window each time.
public boolean checkSubarraySum(int[] nums, int k) { int n = nums.length; for (int start = 0; start < n; start++) { long sum = nums[start]; for (int end = start + 1; end < n; end++) { sum += nums[end]; if (sum % k == 0) return true; // length >= 2 guaranteed } } return false; }
Obviously correct, but quadratic — times out at n = 105.
OptimalPrefix Remainder → Earliest Index
O(n)Space O(min(n, k))Walk the array keeping rem = prefixSum % k. Seed the map with
remainder 0 at index -1 (the empty prefix). If the current
remainder was seen at index p and i - p ≥ 2, the
subarray p+1..i works. Store only the first index of each remainder so
gaps are as long as possible.
public boolean checkSubarraySum(int[] nums, int k) { Map<Long, Integer> firstSeen = new HashMap<>(); firstSeen.put(0L, -1); // empty prefix long rem = 0; for (int i = 0; i < nums.length; i++) { rem = (rem + nums[i]) % k; Integer prev = firstSeen.get(rem); if (prev == null) { firstSeen.put(rem, i); // keep the earliest index only } else if (i - prev >= 2) { return true; } } return false; }
Dry runnums = [23,2,4,6,7], k = 6
- init232467map {0:-1}
- i=023i2467rem=5, map {5:0}
- i=1232i467rem=1, map {1:1}
- i=22324i67rem=5 seen at 0, gap 2
- done232467[2,4] → true ✔
One pass with a hash map; the standard “equal remainders” trick.
[0,0].At a glance
| Level | Approach | Time | Space |
|---|---|---|---|
| Brute Force | All Subarrays with Running Sum | O(n2) | O(1) |
| Optimal | Prefix Remainder → Earliest Index | O(n) | O(min(n, k)) |
- Forgetting the
{0: -1}seed — misses subarrays that start at index 0. - Updating the stored index on every hit — breaks the length ≥ 2 check (e.g.
[5,0,0]). - Treating a single element divisible by
kas valid — length must be ≥ 2.
- Derive it:
(P[j] - P[i]) % k == 0 ⇔ P[j] % k == P[i] % k. - Explain why a map (index) is needed instead of a set: the length constraint.
- Mention handling negatives with
((x % k) + k) % kif inputs could be negative.