Subarray Sums Divisible by K
Problem
Given an integer array and an integer , return the number of non-empty subarrays that have a sum divisible by .
A subarray is a contiguous part of an array.
Example
Input: nums = [4,5,0,-2,-3,1], k = 5
Output: 7
Explanation: 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]
Input: nums = [5], k = 9
Output: 0
Constraints
Submit your solution at here
Solution
Naive brute force (AC, almost TLE)
- First idea come to my mind is that I could traverse all subarray and count, the complexity is
- I can make it by using a prefix sum (after preparation, sum calculation become )
- I realize that if sum of divisable by , and next to it is also divisable by , then is also divisable by , I can cut off some calculation if the array happen to have consecutive segments like that
Complexity
Leetcode’s time limit is pretty generous so my naive solution luckily AC
- Time complexity:
- Space complexity:
- Running time:
class Solution {
public:
int subarraysDivByK(vector<int>& nums, int k) {
int n = nums.size();
vector<int> prefix(n+1, 0);
for(int i = 1;i<=n;i++) {
prefix[i] = prefix[i-1]+nums[i-1];
}
vector<bool> vis(n, false);
int ret = 0;
for(int i = 0;i<=n;i++) {
if(vis[i]) {
continue;
}
int segment = 0;
int mid = i;
for(int j = i+1;j<=n;j++) {
int sum = prefix[j] - prefix[mid];
if(sum%k == 0) {
segment++;
mid = j;
vis[j] = true;
}
}
ret += (segment+1)*segment/2;
vis[i] = true;
}
return ret;
}
};
Intuition
Turned out the intended solution is so simple and elegant. You traverse the prefix sum array. Maintain array , with counts the number of times remainder appears until this point.
- At , your , you should look backward and see how many way to deduce from the to make it divisable
- Thus, with added to the array and , it would makes more subarrays
Complexity
- Time complexity:
- Space complexity:
- Running time:
Code
class Solution {
public:
int subarraysDivByK(vector<int>& nums, int k) {
int n = nums.size();
vector<int> prefix(n+1, 0);
for(int i = 1;i<=n;i++) {
prefix[i] = prefix[i-1]+nums[i-1];
}
int ret = 0;
vector<int> remainderCount(k, 0);
for(int i = 1;i<=n;i++) {
int remainder = (k + (prefix[i] % k)) % k;// double mod to avoid negative remainder
ret += remainderCount[remainder];
remainderCount[remainder]++;
}
ret += remainderCount[0];
return ret;
}
};