Given an array A
of integers, return the number of (contiguous, non-empty) subarrays that have a sum divisible by K
.
Example 1:
Input: A = [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]
Note:
1 <= A.length <= 30000
-10000 <= A[i] <= 10000
2 <= K <= 10000
這道題給了一個數組,讓返回數組的數字之和可以被K整除的非空子數組的個數。博主最開始的思路是建立累加和數組,然后就可以快速的知道任意一個子數組的數字和,從而可以判斷其是否可以被K整除。但是這個方法被 OJ 殘忍拒絕,說是超時 TLE 了,看來需要進一步的將平方級的復雜度優化到線性復雜度才行。說到查詢的線性復雜度,那么 HashMap 是當仁不讓的,可是這里該如何利用它呢。這里首先要搞懂一個規律,若子數組 [0, i] 的數字之和跟子數組 [0, j] 的數字之和對K取余相同的話,假設這里 j > i,那么子數組 [i+1, j] 的數字之和一定是可以整除K的。這里就不證明了,舉個例子吧,比如 [1, 2, 3, 4],K=5,那么 [1] 之和除以5余1,[1, 2, 3] 之和除以5也余1,則 [2, 3] 之和一定可以整除5。有了這些知識,就可以建立數組和除以K的余數跟其出現次數之間的映射了,注意由於數組中可能出現負數,而我們並不希望出現負余數,所以先對K余數,然后再加個K,再對K取余數,這樣一定可以得到正余數。在聲明了 HashMap 后,初始化時要把 0 -> 1
先放進去,原因在后面會講。同時新建變量 sum,用來保存當前的數組和對K的余數,遍歷數組A中的數字 num,根據之前說的,num 先對K取余,然后再加上K,之和再加上 sum,再對K取余。此時將 sum 對映射值加到結果 res 中,這里就有兩種情況,一種是 sum 並不存在,這樣映射值默認是0,另一種是 sum 存在,則根據之前的規律,一定可以找到相同數目的子數組可以整除K,所以將映射值加入結果 res,同時要將 sum 的映射值自增1。這里解釋一下為啥剛開始初始化0的映射值是1,因為若 sum 正好是0了,則當前的數組就是符合的題意的,若0的映射值不是1,則這個數組就無法被加入到結果 res 中,參見代碼如下:
解法一:
class Solution {
public:
int subarraysDivByK(vector<int>& A, int K) {
int res = 0, sum = 0;
unordered_map<int, int> m{{0, 1}};
for (int num : A) {
sum = (sum + num % K + K) % K;
res += m[sum];
++m[sum];
}
return res;
}
};
由於K的余數個數是確定的,所以也可以用個數組來代替 HashMap,其他的部分沒啥區別,解題思想也和上面完全一樣,參見代碼如下:
解法二:
class Solution {
public:
int subarraysDivByK(vector<int>& A, int K) {
int res = 0, sum = 0;
vector<int> cnt(K);
cnt[0] = 1;
for (int num : A) {
sum = (sum + num % K + K) % K;
res += cnt[sum];
++cnt[sum];
}
return res;
}
};
Github 同步地址:
https://github.com/grandyang/leetcode/issues/974
類似題目:
Make Sum Divisible by P
參考資料:
https://leetcode.com/problems/subarray-sums-divisible-by-k/