974. Subarray Sums Divisible by K - Explanation
Description
You are given an integer array nums and an integer k, return the number of non-empty subarrays that have a sum divisible by k.
A subarray is a contiguous part of an array.
Example 1:
Input: nums = [4,5,0,-2,-3,1], k = 5
Output: 7Explanation: There are 7 subarrays with a sum divisible by k = 5:
[4, 5, 0, -2, -3, 1], [5], [5, 0], [5, 0, -2, -3], [0], [0, -2, -3], [-2, -3]
Example 2:
Input: nums = [5], k = 9
Output: 0Constraints:
1 <= nums.length <= 30,000-10,000 <= nums[i] <= 10,0002 <= k <= 10,000
Topics
Prerequisites
Before attempting this problem, you should be comfortable with:
- Prefix Sums - The solution uses prefix sums to efficiently compute subarray sums
- Modular Arithmetic - Understanding how remainders work, especially that two numbers with the same remainder mod k have a difference divisible by k
- Hash Maps - Used to count occurrences of each remainder for efficient lookup
1. Brute Force
Intuition
The simplest approach checks every possible subarray. For each starting index, we extend the subarray one element at a time, keeping a running sum. If the sum is divisible by k (i.e., sum % k == 0), we count it.
Algorithm
- Initialize
res = 0. - For each starting index
i:- Set
curSum = 0. - For each ending index
jfromiton - 1:- Add
nums[j]tocurSum. - If
curSum % k == 0, incrementres.
- Add
- Set
- Return
res.
Time & Space Complexity
- Time complexity:
- Space complexity:
2. Prefix Sum + Hash Map
Intuition
If two prefix sums have the same remainder when divided by k, their difference is divisible by k. So the subarray between those two positions has a sum divisible by k. We use a hash map to count how many times each remainder has appeared. For each new prefix sum, we check how many previous prefix sums share the same remainder.
Algorithm
- Initialize
prefixSum = 0,res = 0, and a hash mapprefixCntwith{0: 1}. - For each number in the array:
- Add it to
prefixSum. - Compute
remain = prefixSum % k. Handle negative remainders by addingkif needed. - Add
prefixCnt[remain]tores. - Increment
prefixCnt[remain]by1.
- Add it to
- Return
res.
class Solution:
def subarraysDivByK(self, nums: List[int], k: int) -> int:
prefix_sum = 0
res = 0
prefix_cnt = defaultdict(int)
prefix_cnt[0] = 1
for n in nums:
prefix_sum += n
remain = prefix_sum % k
res += prefix_cnt[remain]
prefix_cnt[remain] += 1
return resTime & Space Complexity
- Time complexity:
- Space complexity:
3. Prefix Sum + Array
Intuition
Since remainders when dividing by k are always in the range [0, k-1], we can use a fixed-size array instead of a hash map. This gives slightly better constant factors. We also handle negative numbers by adding k to the modulo result, ensuring all remainders are non-negative.
Algorithm
- Create an array
countof sizek, initialized to zeros, withcount[0] = 1. - Initialize
prefix = 0andres = 0. - For each number in the array:
- Update
prefix = (prefix + num % k + k) % kto handle negatives. - Add
count[prefix]tores. - Increment
count[prefix].
- Update
- Return
res.
Time & Space Complexity
- Time complexity:
- Space complexity:
Common Pitfalls
Mishandling Negative Remainders
In most programming languages, the modulo of a negative number can return a negative result (e.g., -5 % 3 = -2 in Java/C++). You must normalize the remainder to be non-negative by using ((prefixSum % k) + k) % k to ensure correct hash map lookups.
Confusing This Problem with Subarray Sum Equals K
While both problems use prefix sums, the comparison logic differs. Here you need to match remainders, not exact differences. Two prefix sums with the same remainder modulo k indicate a valid subarray, regardless of their absolute values.
Forgetting to Initialize Count for Remainder Zero
The hash map or array must start with count[0] = 1 to account for subarrays starting from index 0 that are directly divisible by k. Missing this initialization causes undercounting.
Sign in to join the discussion