Continuous Subarray Sum
Table of Contents + β
You want to find a run of numbers that adds up to a multiple of k. Checking every run is slow. The interviewer wants to see if you know a remainder trick that makes it fast. That trick is the whole point of this question.
π― The Problem
You get an array and a number k. Say if some run of numbers sums to a multiple of k.
- A run of numbers next to each other is a subarray.
- The run must have at least two numbers.
- Its sum must be a multiple of
k. So dividing it bykleaves no leftover. - Return
trueif such a run exists. Otherwisefalse. - A single number that happens to be a multiple of
kdoes not count.
Say the array is [23, 2, 4, 6, 7] and k is 6. Look at 2 + 4, which is 6. That is a multiple of 6. The run has two numbers. So the answer is true.
Input: nums = [23, 2, 4, 6, 7], k = 6Output: true
Explanation: the subarray [2, 4] sums to 6, which is a multiple of 6.Here is the array with the matching run marked.
π’ Approach 1: Try Every Run (Brute Force)
The idea in one line: check every run and test its sum.
The idea:
- Pick a start.
- Grow the run to the right one number at a time.
- Keep a running sum and test it each step.
How it works:
- One loop picks the start position.
- A second loop adds numbers going forward.
- Test the sum each time.
- If any run of length two or more is a multiple of
k, return true.
Why it is weak:
- There are about n times n runs to check.
- That is O(nΒ²) work.
- A big array makes this slow.
Here is the brute-force code for that idea:
def check_subarray_sum(nums, k): for start in range(len(nums)): total = 0 for end in range(start, len(nums)): total += nums[end] if end - start + 1 >= 2 and total % k == 0: return True
return False
print(check_subarray_sum([23, 2, 4, 6, 7], 6))β‘ Approach 2: Prefix Sum Mod K With a Hash Map (Best)
The idea in one line: two prefix sums with the same remainder mean the run between them is a multiple of k.
The idea:
- A prefix sum is the total from the start up to the current spot.
- The sum of a run equals the prefix sum at the end minus the prefix sum at the start.
- Store each prefix sumβs remainder mod
k, not the raw sum. - The remainder is what is left over after dividing by
k. - Equal remainders cancel out, so the run between them is a multiple of
k.
How it works:
- Walk the array once, keeping a running prefix sum.
- At each step, take its remainder mod
k. - Store each remainder in a hash map with the first index where you saw it.
- A hash map looks up a key almost instantly.
- See a remainder again with a gap of at least two, and the answer is true.
- Seed the map with remainder
0at index-1to catch a run that starts at the very beginning. - Keep only the first index for each remainder, so the gap stays as wide as possible.
Why it is fast:
- One pass with fast lookups. That is O(n) time.
- The map holds at most
kremainders. That is O(k) space.
Here is the dry run on [23, 2, 4, 6, 7] with k = 6.
Steps to Solve
- Create a hash map and put remainder
0at index-1. - Walk the array, keeping a running prefix sum.
- At each index, take the remainder of the prefix sum mod
k. - If that remainder is already in the map, and the gap to the stored index is at least two, return true.
- If the remainder is new, store it with the current index.
- If the walk ends with no match, return false.
This Python version uses a dictionary from remainder to the first index it was seen.
def check_subarray_sum(nums, k): first = {0: -1} # remainder 0 sits before the array starts total = 0 for i, num in enumerate(nums): total += num rem = total % k # remainder of the prefix sum if rem in first: if i - first[rem] >= 2: # run of length 2 or more return True else: first[rem] = i # store the first index for this rem return False
nums = [23, 2, 4, 6, 7]print(check_subarray_sum(nums, 6))The output of the above code will be:
TrueLet us walk through the Python version line by line. The line first = {0: -1} seeds the map with remainder 0 at index -1. This covers a run from the very start that is already a multiple of k. Without it, such a run would be missed.
The loop for i, num in enumerate(nums) walks the array with both the index and the value. The line total += num keeps the running prefix sum. So after 23 and 2 and 4, the total is 29.
The line rem = total % k takes the remainder. For total 29 and k = 6, the remainder is 5. We store remainders, not raw sums, because two equal remainders mean the run between them is a multiple of k.
The check if rem in first asks if we saw this remainder before. We saw remainder 5 earlier at index 0, after the number 23. Now we are at index 2. The line if i - first[rem] >= 2 confirms the gap is at least two, so the run has two or more numbers. The gap from 0 to 2 is 2, so we return True. If the remainder is new, the else branch stores it with the current index. We keep only the first index so the gap stays as wide as possible.
β±οΈ Time and Space Complexity
The brute force checks every run, so it is O(nΒ²). The prefix sum plus hash map walks the array one time with fast lookups, so it is O(n). The map can hold up to k different remainders, so the space is O(k).
| Approach | Time Complexity | Space Complexity |
|---|---|---|
| Brute force (every run) | O(nΒ²) | O(1) |
| Prefix sum mod k with hash map | O(n) | O(k) |
Tip
The insight to say out loud is that two prefix sums with the same remainder make a run that is a multiple of k. Once you name that, the hash map falls into place. The interviewer wants to hear that remainder idea.
π§© Key Takeaways
- β A run that is a multiple of k shows up as two prefix sums with the same remainder.
- β Store remainders in a hash map, not the raw sums.
- β Seed the map with remainder 0 at index -1 to catch a run from the start.
- β Keep only the first index for each remainder so the gap stays wide.
- β This turns an O(nΒ²) search into one O(n) pass.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
What must be true about the subarray you are looking for?
Why: The run must have two or more numbers and its sum must be a multiple of k.
- 2
What do we store in the hash map for this problem?
Why: We store each prefix sum's remainder mod k with the first index where that remainder was seen.
- 3
Why do we seed the map with remainder 0 at index -1?
Why: Remainder 0 at index -1 lets a multiple-of-k run that begins at the start be detected.
- 4
If two prefix sums share the same remainder mod k, what does that mean?
Why: Equal remainders mean the leftover parts cancel, so the run between them is a multiple of k.