Problem Link: 974. Subarray Sums Divisible by K.
Function definition:
/**
* @param {number[]} nums
* @param {number} k
* @return {number}
*/
var subarraysDivByK = function (nums, k) {}
Brute Force (TLE)
The first intuition for solving subarray problems is using prefix sum. For , define as
Then for , we just need to count how many s fulfill . The time complexity is and the space complexity is .
Count Remainders
Consider the requirement above
We can make some transformations
So for , if , then it fulfills the requirement. We define a map called remainderToCount
to store the count of prefix sums’ remainders.
Once we have , we can pick from subarrays to get a required subarray. The number of subarrays we can get is A special case is . If , any one of the subarrays also fulfills the requirement. So we can pick or from subarrays to get a required subarray. The number of subarrays we can get is Note that for some programming languages, is not equivalent to but equivalent to as and .
Implementation
/**
* @param {number[]} nums
* @param {number} k
* @return {number}
*/
var subarraysDivByK = function (nums, k) {
const N = nums.length;
/** @type {Map<number, number>} */
const remainderToCount = new Map();
let currentPrefixSum = 0;
let subArrayCount = 0;
for (let i = 0; i < N; i++) {
currentPrefixSum += nums[i];
const reminder = ((currentPrefixSum % k) + k) % k;
remainderToCount.set(
reminder,
(remainderToCount.get(reminder) ?? 0) + 1,
);
}
for (const [, count] of remainderToCount) {
// prevent overflow
subArrayCount +=
count % 2 === 1
? count * ((count - 1) / 2)
: (count / 2) * (count - 1);
}
// spacial case of remainder = 0
subArrayCount += remainderToCount.get(0) ?? 0;
return subArrayCount;
};
The popular solution on LeetCode is equivalent to the solution above. It calculates subArrayCount
while calculating currentPrefixSum
and handles the special case before the calculation.
/**
* @param {number[]} nums
* @param {number} k
* @return {number}
*/
var subarraysDivByK = function (nums, k) {
const N = nums.length;
/** @type {Map<number, number>} */
const remainderToCount = new Map();
// Every time we get a subarray fulfills currentPrefixSum % k === 0,
// we additionally add the current subarray to subArrayCount
remainderToCount.set(0, 1);
let currentPrefixSum = 0;
let subArrayCount = 0;
for (let i = 0; i < N; i++) {
currentPrefixSum += nums[i];
const reminder = ((currentPrefixSum % k) + k) % k;
subArrayCount += remainderToCount.get(reminder) ?? 0;
remainderToCount.set(
reminder,
(remainderToCount.get(reminder) ?? 0) + 1,
);
}
return subArrayCount;
};