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.
For example:
nums = [4, 5, 0, -2, -3, 1], k = 5
The output should be 7 because the following subarrays have a sum divisible by k = 5:
[4, 5, 0, -2, -3, 1]
(sum is 5)[5]
(sum is 5)[5, 0]
(sum is 5)[5, 0, -2, -3]
(sum is 0)[0]
(sum is 0)[0, -2, -3]
(sum is -5)[-2, -3]
(sum is -5)Another example:
nums = [5], k = 9
The output should be 0.
How would you approach this problem? Consider the time and space complexity of your solution. Can you provide both a brute force solution and a more optimized solution?
The most straightforward approach is to check every possible subarray and count the ones whose sum is divisible by k
.
k
. If it is, increment the count.def subarraysDivByK_brute_force(nums, k):
count = 0
n = len(nums)
for i in range(n):
for j in range(i, n):
sub_array_sum = sum(nums[i:j+1])
if sub_array_sum % k == 0:
count += 1
return count
Time Complexity: O(n^2) due to the nested loops.
Space Complexity: O(1) as we are only using a constant amount of extra space.
We can use the concept of prefix sums and the property that (a - b) % k == 0
if and only if a % k == b % k
. We store the remainders in a hash map or an array.
k
. Initialize the count of remainder 0 to 1, as an empty subarray has a sum of 0, which is divisible by any k
.nums
array, keeping track of the cumulative sum.k
.k
to it to make it non-negative (since negative remainders in Python can be different from other languages).def subarraysDivByK(nums, k):
count = 0
prefix_sums = {0: 1}
curr_sum = 0
for num in nums:
curr_sum = (curr_sum + num) % k
if curr_sum < 0:
curr_sum += k
if curr_sum in prefix_sums:
count += prefix_sums[curr_sum]
prefix_sums[curr_sum] += 1
else:
prefix_sums[curr_sum] = 1
return count
Time Complexity: O(n) as we iterate through the array only once.
Space Complexity: O(k) in the worst case, as the hash map can store at most k
different remainders (0 to k-1).
k
to the remainder to make it non-negative.k
, so integer overflow is not a major concern, but it's always good to keep in mind for extremely large inputs.nums = [4, 5, 0, -2, -3, 1], k = 5
curr_sum = 4, remainder = 4
. prefix_sums = {0: 1, 4: 1}
curr_sum = 9, remainder = 4
. count += 1
, prefix_sums = {0: 1, 4: 2}
curr_sum = 9, remainder = 4
. count += 1
, prefix_sums = {0: 1, 4: 2}
curr_sum = 9, remainder = 4
. count += 1
, prefix_sums = {0: 1, 4: 2}
curr_sum = 9, remainder = 4
. count += 1
, prefix_sums = {0: 1, 4: 2}
curr_sum = 9, remainder = 4
. count += 1
, prefix_sums = {0: 1, 4: 2}