【算法拾光】前缀和|连续的子数组和 + 和相同的二元子数组

题目 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;
  • 哈希表不存余数出现次数,存余数第一次出现的下标。 思路:
  1. 哈希表保存 余数 -> 最早下标;初始化 mp[0] = -1
  2. 遍历,计算当前前缀和的余数 mod = (preSum % k + k) % k
  3. 如果余数已经在哈希表中:判断当前下标和保存的下标差值是否≥2,满足直接返回 true
  4. 如果余数不存在,才存入哈希表(只保留最早下标,保证长度尽可能大)

代码编写

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;
    }
};

本篇总结

  1. 523 连续的子数组和:同余定理变种,重点约束子数组长度≥2,哈希存余数第一次出现下标,不要重复更新下标;
  2. 930 和相同的二元子数组:和 560 模板同源,preSum[j]=preSum[i]-goal,统计满足条件的子数组数量;
  3. 对比区分:
    • 计数类:哈希存次数(560、974、930)
    • 判断存在 + 长度限制:哈希存最早下标(523)
  4. 小提醒:只要涉及前缀和取模,记得 (preSum % k + k) % k 处理负数。
相关推荐
Pniubi2 小时前
力扣 102二叉树的层序遍历(广度优先搜索)
leetcode·宽度优先
一去不复返的通信er2 小时前
力扣15:盛最多水的容器
算法·leetcode
Navigator_Z2 小时前
LeetCode //C - 1309. Decrypt String from Alphabet to Integer Mapping
c语言·算法·leetcode
潇湘秦2 小时前
SQL Server 2016+ 整数类型元数据扩宽(秒级变更)完整说明
数据库·算法
文丑颜不良啊2 小时前
LeetCode1281~1300题目解析
java·leetcode
Figo_Cheung3 小时前
Figo基于结构空间算法假设(SSAH)的生成式大模型优化范式与工程实现边界研究——提出适应AGI架构演化的自适应拓扑感知表征机制
算法·架构·agi
旖旎夜光3 小时前
LCR 170:交易逆序对的总数(归并排序) —— 题解
c++·学习·算法·leetcode·排序算法
微尘hjx12 小时前
【ByteTrack 04】多轨迹算法ByteTrack改进点
算法·yolo·目标检测·目标跟踪·bytetrack·ultralytics·车跟踪