当前位置: 首页 > news >正文

前缀和的利用 前缀和的扩展问题

利用前缀和:快速找到所有满足要求的子数组数量&&变种问题

  • 何为前缀和:对数组元素做累加和
  • 如何用前缀和在一次O(N)找到所有符合要求的子数组数量?
    • 步骤 1 :求出前缀和(见第一节)
    • 步骤 2 :在前缀和上进行遍历,找到所有目标值
    • 步骤 3 :循环次数优化
    • 步骤 4 :初始值
    • 实现
  • 变种问题(同类问题,加一些帽子)
    • 步骤 1 :转化问题
    • 步骤 2 :套用上一题
    • 步骤 3 :实现

何为前缀和:对数组元素做累加和

按照以下示例展示前缀和计算方法:
注意!!!!:前缀和数组第一个元素永远都是0,累加和从第二处开始添加

应用场景:求任意子数组 / 求数组内任意一个连续范围内 的和

初始状态:
1、nums = [1, 1, -1, 1, -1]
2、前缀和数组 s = [0, 0, 0, 0, 0, 0]。这是因为我们需要一个多一位的数组,其中 s[0] = 0 代表没有元素的前缀和。

计算前缀和数组 s:
迭代1:
nums[0] = 1,计算 s[[1]]= s[0] + 1 = 0 + 1 = 1,前缀和更新为 s = [0, 1, 0, 0, 0, 0]。
迭代2:
nums[[1]] = 1,计算 s[[2]] = s[[1]] + 1 = 1 + 1 = 2,前缀和更新为 s = [0, 1, 2, 0, 0, 0]。
迭代3:
nums[[2]] = -1,计算 s[[3]] = s[[2]] + (-1) = 2 - 1 = 1,前缀和更新为 s = [0, 1, 2, 1, 0, 0]。
迭代4:
nums[[3]] = 1,计算 s[[4]] = s[[3]] + 1 = 1 + 1 = 2,前缀和更新为 s = [0, 1, 2, 1, 2, 0]。
迭代5:
nums[[4]] = -1,计算 s[[5]] = s[[4]] + (-1) = 2 - 1 = 1,前缀和更新为 s = [0, 1, 2, 1, 2, 1]。

如何用前缀和在一次O(N)找到所有符合要求的子数组数量?

我们以 力扣560. 和为 K 的子数组 为例对该问题进行求解
在这里插入图片描述

步骤 1 :求出前缀和(见第一节)

步骤 2 :在前缀和上进行遍历,找到所有目标值

如何在前缀和上找到任意一个子数组的和:s[j] - s[i] = SUM
本题要求和为K,所以转化成:s[j] - s[i] = k
1、一个朴素的想法就是,枚举J的位置,再从0处枚举i,找到一个前缀和s[i]的值满足:s[i] = s[j] - k
2、但是显而易见的,复杂度=O(n^2),对于本题而言不可以,那么有没有什么办法可以省略掉第二次循环?
3、第二次循环的目的是什么?找到满足要求的所有s[i]的数量,所以我们动态维护就好了,采用一个字典维护出现过的所有前缀和的值以及他们的出现次数,这样就可以在O(1)的时间内得到满足要求的数量
4、这时,只需要遍历完所有元素,累加出的和即为答案。

步骤 3 :循环次数优化

很显然,对于每次的s[j]前面的元素,我已经不需要存储在数组中,只需要维护在HashMap中即可,我每次先更新此处的前缀和值,再根据s[i] = s[j] - k 找到s[i]的值,最后去HashMap中找到s[i]出现了几次,最后做累加即可。所以显而易见的,可以边算前缀和边统计

步骤 4 :初始值

满足上述计算条件,需要从nums[0]也就是s[1]开始,所以要把s[0]=0初始状态加到HashMap中,即初始值HashMap[0] = 1

实现

class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        int ans = 0;
        unordered_map<int, int> cnt{ { 0,1 } };//注意写法
        int s = 0;
        for (int x : nums) {
            s += x;//先计算此处前缀和的值
            ans += cnt.find(s - k) == cnt.end() ? 0 : cnt[s - k];//在寻找s[i]出现几次,这里注意,不要直接调用cnt[s - k],因为没有cnt[s - k]的话会插入
            cnt[s]++;//s出现次数多一次,在字典中为其更新
        }
        return ans;
    }
};

变种问题(同类问题,加一些帽子)

我们以 力扣2588. 统计美丽子数组数目 为例对该问题进行求解
在这里插入图片描述

步骤 1 :转化问题

很明显的,从十进制看不出任何规律,并且,题目中明确提示了二进制,所以把例一转化一下,把每个数都写成二进制:

													100
													011
													001
													010
													100

​我们此时再看例一中两个符合要求的组,很明显的,以[4,3,1,2,4]为例,我们发现各个列都是2个1,另一个美丽子数组也同样。所以,很明显,体重操作就是执行异或操作,只要选择的数如上述摆放,每一列偶数个1就满足要求。

换句话说:满足要求的子数组,其所有元素的异或和 = 0

所以本题转化为:找到满足子数组所有元素异或和 = 0的子数组个数

步骤 2 :套用上一题

把上一题中 k 设置为 0 即可
+ 运算改为 ^ 即可

步骤 3 :实现

class Solution {
public:
    long long beautifulSubarrays(vector<int>& nums) {
        long long ans = 0;
        unordered_map<int, int> cnt{{0, 1}};
        int s = 0;
        for (int x : nums) {
            s ^= x;
            ans += cnt[s - 0]++;
        }
        return ans;
    }
};

相关文章:

  • Figma 对图片进行模糊处理
  • 【记录】Python3|Linux下安装Virtualenv和virtualenvwrapper用于处理虚拟环境
  • nodejs去除本地文件html字符
  • 【蓝桥杯】每天一题,理解逻辑(3/90)【Leetcode 快乐数】
  • 利用 ArcGIS Pro 快速统计省域各市道路长度的实操指南
  • 专业工具,提供多种磁盘分区方案
  • Harmony os next~HarmonyOS Ability与页面跳转开发详解
  • 深入理解 C# 特性(Attributes):概念、应用与自定义
  • VS2022C#windows窗体应用程序调用DeepSeek API
  • Vue父子组件传递笔记
  • PostgreSQL 安装与使用
  • 安装与配置 STK-MATLAB 接口
  • 互联网摸鱼日报(2025-03-04)
  • android11使用gpio口控制led状态灯
  • 6. PromQL的metric name(在node exporter复制下来交给AI解释的)
  • k8s v1.28.15部署(kubeadm方式)
  • GAT从理论到实践——基于图注意力网络的节点特征计算与表示
  • 【运维笔记】Navicat中删除mongo 某个时间之前的数据
  • 分布式存储—— HBase数据模型 详解
  • 创建阿里云CDN
  • 辽宁援疆前指总指挥王敬华已任新疆塔城地委副书记
  • 武康大楼再开发:一栋楼火还不够,要带火街区“朋友圈”
  • 上海比常年平均时间提前12天入夏,明天最高气温可达33℃
  • 李伟任山东省委常委、省纪委书记
  • 银行积分大幅贬值遭质疑,涉及工行、中行、农行等
  • 曾犯强奸罪教师出狱后办教培机构?柳州鱼峰区教育局回应