Continuous Subarray Sum

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 by k leaves no leftover.
  • Return true if such a run exists. Otherwise false.
  • A single number that happens to be a multiple of k does 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 = 6
Output: true
Explanation: the subarray [2, 4] sums to 6, which is a multiple of 6.

Here is the array with the matching run marked.

2 + 4 = 6 multiple of k

23

2

4

6

7

🐒 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:

continuous_subarray_sum_brute_force.py
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 0 at index -1 to 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 k remainders. That is O(k) space.

Here is the dry run on [23, 2, 4, 6, 7] with k = 6.

start: remainder 0 at index -1

after 23: sum 23, rem 5, store 5

after 2: sum 25, rem 1, store 1

after 4: sum 29, rem 5, seen before at 0

gap from 0 to 2 is 2, length ok

answer true

Steps to Solve

  1. Create a hash map and put remainder 0 at index -1.
  2. Walk the array, keeping a running prefix sum.
  3. At each index, take the remainder of the prefix sum mod k.
  4. If that remainder is already in the map, and the gap to the stored index is at least two, return true.
  5. If the remainder is new, store it with the current index.
  6. If the walk ends with no match, return false.

This Python version uses a dictionary from remainder to the first index it was seen.

continuous_subarray.py
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:

True

Let 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

4 questions Show quiz Hide quiz

Test what you learned. Pick an answer for each question, then click Check.

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

πŸš€ What’s Next?