
题目 1:连续的子数组和
题目链接:523. 连续的子数组和 - 力扣(LeetCode)
题目分析
给你一个整数数组 nums 和一个整数 k ,编写一个函数来判断该数组是否含有同时满足下述条件的连续子数组:
- 子数组长度至少为 2
- 子数组的元素之和是
k的倍数 也就是和 % k == 0。 如果存在,返回 true ;否则,返回 false。
示例:
cpp
nums = [23,2,4,6,7], k = 6
输出:true
解释:[2,4] 是一个大小为 2 的子数组,并且和为 6
算法原理
延续同余定理: (preSum[i] - preSum[j]) % k == 0 ⇒ preSum[i] % k == preSum[j] % k 和上一题 974 的区别:
- 本题不需要计数,只需要判断是否存在;
- 增加限制:子数组长度 ≥ 2,也就是下标差
i - j ≥ 2; - 哈希表不存余数出现次数,存余数第一次出现的下标。 思路:
- 哈希表保存
余数 -> 最早下标;初始化mp[0] = -1 - 遍历,计算当前前缀和的余数
mod = (preSum % k + k) % k - 如果余数已经在哈希表中:判断当前下标和保存的下标差值是否≥2,满足直接返回 true
- 如果余数不存在,才存入哈希表(只保留最早下标,保证长度尽可能大)
代码编写
cpp
class Solution {
public:
bool checkSubarraySum(vector<int>& nums, int k) {
unordered_map<int, int> mp;
mp[0] = -1;
int preSum = 0;
for(int i = 0; i < nums.size(); i++){
preSum += nums[i];
int mod = (preSum % k + k) % k;
if(mp.count(mod)){
if(i - mp[mod] >= 2){
return true;
}
}else{
mp[mod] = i;
}
}
return false;
}
};
题目 2:和相同的二元子数组
题目链接:930. 和相同的二元子数组 - 力扣(LeetCode)
题目分析
给你一个二元数组 nums,数组中元素只有 0 和 1,以及一个整数 goal。 求有多少个连续子数组,子数组的和等于 goal。
示例:
cpp
nums = [1,0,1,0,1], goal = 2
输出:4
注意:数组里有大量 0,0 不会改变前缀和,会产生很多合法子数组。
算法原理
公式依旧:preSum[i] - preSum[j] = goal ⇒ preSum[j] = preSum[i] - goal 和 560【和为 K 的子数组】逻辑几乎一致,只是数组限定只有 0、1。
- 哈希表记录每个前缀和出现的次数;
- 初始
mp[0] = 1; - 遍历累加前缀和,每次加上
preSum - goal对应的数量;
补充:这题也可以双指针,但前缀和 + 哈希写法更通用,不受元素限制。
代码编写
cpp
class Solution {
public:
int numSubarraysWithSum(vector<int>& nums, int goal) {
unordered_map<int, int> mp;
mp[0] = 1;
int preSum = 0;
int ans = 0;
for(int x : nums){
preSum += x;
if(mp.count(preSum - goal)){
ans += mp[preSum - goal];
}
mp[preSum]++;
}
return ans;
}
};
本篇总结
- 523 连续的子数组和:同余定理变种,重点约束子数组长度≥2,哈希存余数第一次出现下标,不要重复更新下标;
- 930 和相同的二元子数组:和 560 模板同源,
preSum[j]=preSum[i]-goal,统计满足条件的子数组数量; - 对比区分:
- 计数类:哈希存次数(560、974、930)
- 判断存在 + 长度限制:哈希存最早下标(523)
- 小提醒:只要涉及前缀和取模,记得
(preSum % k + k) % k处理负数。
