文章目录
- 🪁1. 题目
- 🎣2. 算法原理
- 🪄解法一:暴力枚举
- 🪄解法二:前缀和 + 哈希表
- ⛳3. 代码实现
🪁1. 题目
题目链接:974. 和可被 K 整除的子数组 - 力扣(LeetCode)
给定一个整数数组 nums
和一个整数 k
,返回其中元素之和可被 k
整除的(连续、非空) 子数组 的数目。
子数组 是数组的 连续 部分。
示例 1:
输入:nums = [4,5,0,-2,-3,1], k = 5
输出:7
解释:
有 7 个子数组满足其元素之和可被 k = 5 整除:
[4, 5, 0, -2, -3, 1], [5], [5, 0], [5, 0, -2, -3], [0], [0, -2, -3], [-2, -3]
示例 2:
输入: nums = [5], k = 9
输出: 0
提示:
1 <= nums.length <= 3 * 104
-104 <= nums[i] <= 104
2 <= k <= 104
🎣2. 算法原理
🪄解法一:暴力枚举
暴力枚举出所有的子数组,然后判断是否有符合条件的,时间复杂度为O(n2)。
本题不可使用滑动窗口,因为这里面包含了负数和
0
,不具有单调性
🪄解法二:前缀和 + 哈希表
Tips:
- 同余定理:
(a-b)/p = k......0
,当a-b
的差能够被p
整除,那么a%p = b%p
- 在C++中,负数%正数的结果为负数,如果要将其修正,只需要加上取模的这个数即可
a%p + p
,但为了正负统一,我们再模一下即可(a%p+p)%p
C++和JAVA都需要修正,Python不需要
有了上面的知识,这题其实就和此篇文章的题目类似了:前缀和+哈希表——560. 和为 K 的子数组,有兴趣可以看一下
相当于只要找到以i
为结尾的所有子数组的前缀和这个区间是否有能被k
整除的子数组,即(sum-x) % k = 0
这个正好对应上面的同余定理,所以就变成了找出在[0,i-1]
区间内,有多少个余数等于sum%k
。
注意修正余数
(sum%k+k)%k
这里哈希表里面映射关系就是前缀和的余数和次数。
细节问题:
- 前缀和加入哈希表的时机:不能一股脑将前缀和全部丢进哈希表,我们只需要计算在
i
位置之前,哈希表里面只保存[0,i-1]
位置的前缀和。- 不用真正的创建一个前缀和数组,只需借助一个临时变量,记录这个位置之前的前缀和,计算完毕之后,再更新一下这个临时变量。
- 当整个前缀和为
k
时,我们需要在哈希表中默认有一个余数为0的位置hash[0]=1,以防这个情况被忽略。
⛳3. 代码实现
class Solution {
public:
int subarraysDivByK(vector<int>& nums, int k)
{
unordered_map<int,int> hash;
hash[0] = 1; //0的处理余数
int sum = 0;
int ret = 0;
for(auto e:nums)
{
sum+=e;
int r = (sum%k+k)%k; //修正余数
if(hash.count(r)) ret+=hash[r];
hash[r]++;
}
return ret;
}
};