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

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

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

  • 何为前缀和:对数组元素做累加和
  • 如何用前缀和在一次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;
    }
};

http://www.kler.cn/a/574269.html

相关文章:

  • 如何用HTML5 Canvas实现电子签名功能✍️
  • P8707 [蓝桥杯 2020 省 AB1] 走方格
  • 【智能体架构:Agent】LangChain智能体类型ReAct、Self-ASK的区别
  • 鸿蒙Android4个脚有脚线
  • 道可云人工智能每日资讯|亚马逊云业务部门成立智能体人工智能团队
  • Unity3D WebGL内存优化与缓存管理
  • 使用jcodec库,访问网络视频提取封面图片上传至oss
  • [BD青训项目]介绍
  • vue3中子组件获取父组件的name,父组件不做修改动作
  • 算法探秘:盛最多水的容器问题
  • Oracle数据导入导出小工具(主要用于导入导出小批量含大字段的数据)
  • 快速启动 vue 开发环境
  • 特斯拉FSD(全自动驾驶)功能概述
  • 迷你世界脚本文字板接口:Graphics
  • centos7服务器 Java和Hadoop安装教程,用VMware和finalshell
  • 【C++教程】C++中的基本数据类型
  • 【每日学点HarmonyOS Next知识】Web上传文件、监听上下左右区域连续点击、折叠悬停、字符串相关、播放沙盒视频
  • VsCode/Cursor workbench.desktop.main.js 的入口
  • VS2019,VCPKG - 为VS2019添加VCPKG
  • 【启发式算法】Dijkstra算法详细介绍(Python)