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

“前缀和”专题篇二

目录

和为K的子数组

和可被K整除的子数组

连续数组

矩阵区域和


和为K的子数组

题目

思路

我们可能想到的是,从头到尾扫描数组,然后分别计算以该位置为开始,一直到数组末尾,符合和为K的子数组,但是这种方法的时间复杂度是O(N^2),是会超时的,因此需要别的方法来解决。

可能又会想到利用“前缀和”的思想,但是从头到尾扫描整个数组,然后分别计算以该位置为开始,一直到数组末尾,符合和为K的子数组,这样时间复杂度依旧是O(N^2),是会超时的,因此需要别的方法来解决。

下面我们仔细分析一下,子数组问题可以通过以某个位置为结尾来分析,此时可以先计算出该位置(含该位置)之前所有数的和,这个和是确定的,要想找到以该位置为结尾且和为K的子数组,只需要找出该位置之前和为sum-K的子数组即可,该位置之前和为sum-K的子数组的数量,就是以该位置为结尾且和为K的子数组的个数,这里可以使用“前缀和”思想,不过这里存储的不再单单只是元素的和,而是每个元素的和以及对应的个数,这里需要注意的一点是,我们并不是提前计算好前缀和,而是分析到哪个位置就计算到哪个位置之前的前缀和以及其对应的个数,可以使用哈希表进行存储,方便及时查询到,并且我们可以做一些优化,因为计算前缀和只需要知道前一个位置的前缀和,因此使用一个变量就可以实现计算前缀和。

还有需要注意的是,针对上面的方法,如果所求整个数组的和为sum,那么我们会计算[0,-1]区间的数组的和,因此,我们需要考虑到这一点,处理方法是先将0存放到哈希表中,映射的值是1.

代码

class Solution {
public:int subarraySum(vector<int>& nums, int k) {unordered_map<int,int> hash;hash[0]=1;int sum=0,ret=0;for(int x:nums){sum+=x;if(hash.count(sum-k)) ret+=hash[sum-k];hash[sum]++;}return ret;}
};
和可被K整除的子数组

题目

思路

我们可能想到的是,从头到尾扫描数组,然后分别计算以该位置为开始,一直到数组末尾,符合和可被K整除的子数组,但是这种方法的时间复杂度是O(N^2),是会超时的,因此需要别的方法来解决。

可能又会想到利用“前缀和”的思想,但是从头到尾扫描整个数组,然后分别计算以该位置为开始,一直到数组末尾,符合和可被K整除的子数组,这样时间复杂度依旧是O(N^2),是会超时的,因此需要别的方法来解决。

下面我们仔细分析一下,子数组问题可以通过以某个位置为结尾来分析,此时我们可以先计算该位置(含该位置)之前所有元素的和,题目要求是找出和可被K整除的子数组,因为该位置(含该位置)之前所有元素的和是确定的,如果找出以该位置为结尾,和可被K整除的子数组,假设该位置(含该位置)之前所有元素的和是sum,除了和可被K整除的子数组之外元素的和是sum-n*k,那么根据《同余定理》就有sum%k=(sum-n*k),即我们只需找出该位置之前余数为sum%K的子数组的个数即可,这里需要注意的是,我们不再是先提前计算好前缀和,而是求到哪个位置就计算到哪个位置的前缀和,这里使用变量来代替之前的数组,因为计算前缀和只会用到前一个位置的前缀和和当前位置的元素,并且这里不再只是计算前缀和,还需要计算每个前缀和的值的余数对应的个数,使用哈希表来进行存储,这样就可以更快地查询到每个前缀和的值的余数对应的个数。

因为C++中对负数取模,得到的还是一个负数,因此我们需要做一些处理,即针对sum%K,变换成(sum%K+K)%K。

还有需要注意的是,针对上面的方法,如果所求整个数组的和为sum,那么我们会计算[0,-1]区间的数组余数为(sum%K+K)%K的个数,因此,我们需要考虑到这一点,处理方法是先将0存放到哈希表中,映射的值是1.

代码

class Solution {
public:int subarraysDivByK(vector<int>& nums, int k) {unordered_map<int,int> hash;hash[0%k]=1;int sum=0,ret=0;for(int x:nums){sum+=x;int a=(sum%k+k)%k;if(hash.count(a)) ret+=hash[a];hash[a]++;}return ret;}
};
连续数组

题目

思路

我们可能想到利用“前缀和”的思想,但是这道题是01序列,利用“前缀和”思想并不能知道具体有多少个0,因此我们需要再分析分析,我们不妨将原始数组中的0替换成 -1,这样找出的数组是1和 -1能够相互抵消的。

解决这道题时,我们使用“前缀和”时,不能先提前计算好前缀和,而是分析到哪个位置就把前缀和计算到哪个位置,因为计算每个位置的前缀和只会用到前一个位置的前缀和以及当前位置的值。

需要注意的是,因为题目要求含有相同数量的01的子数组的最长长度,因此下面在计算前缀和时还需要记录每个前缀和值对应的区间末尾最小下标,即如果再次遇到相同前缀和值的时候,不再记录,只记录前缀和值首次出现的区间末尾下标。

代码

class Solution {
public:int findMaxLength(vector<int>& nums) {unordered_map<int,int> hash;hash[0]=-1;int sum=0,ret=0;for(int i=0;i<nums.size();i++){sum+=nums[i]==0?-1:1;if(hash.count(sum)) ret=max(ret,i-hash[sum]);else hash[sum]=i;}return ret;}
};
矩阵区域和

题目

思路

这道题如果使用暴力解法的话,即针对针对查询,算出本次查询的区域,然后再计算出该区域所有数的和,但是这种方法的时间复杂度是O(N*M*Q),是会超时的,因此需要别的方法来解决。

下面将依旧使用“前缀和”的思想来解决这道题,即预先计算出每个位置和左上角构成的矩形的值的总和,针对每次查询,就可以使用O(1)的时间复杂度计算出所查询区域的值的总和。

【1】计算好每个位置和左上角构成的矩形的值的总和

【2】计算出所查询区域的值的总和

其实快速解决这道题是利用二维前缀和,只不过这道题要处理一些边界情况,因为下标[i-k],[i+k],[j-k],[j+k]是有可能超过二维前缀和矩阵的大小的,需要特别处理一下。

代码

class Solution {
public:vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int k) {int m=mat.size(),n=mat[0].size();vector<vector<int>> dp(m+1,vector<int>(n+1));vector<vector<int>> answer(m,vector<int>(n));for(int i=1;i<=m;i++)for(int j=1;j<=n;j++)dp[i][j]=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]+mat[i-1][j-1];for(int i=0;i<m;i++)for(int j=0;j<n;j++){int x1=max(1,i-k+1),y1=max(1,j-k+1);int x2=min(m,i+k+1),y2=min(n,j+k+1);answer[i][j]=dp[x2][y2]-dp[x1-1][y2]-dp[x2][y1-1]+dp[x1-1][y1-1];// answer[i][j]=dp[min(m,i+k+1)][min(n,j+k+1)]-dp[max(0,i-k)][min(n,j+k+1)]-dp[min(m,i+k+1)][max(0,j-k)]+dp[max(0,i-k)][max(0,j-k)];}return answer;}
};

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • “程序员的艺术转身:AI绘画副业,从代码到画布的变现之旅“
  • 【文件IO】文件内容操作
  • jmeter使用while控制器时防止死循环
  • 临床数据科学和金融数据科学,选择R语言吧!
  • Python操作MongoDB文档存储
  • workerman下的webman路由浏览器跨域的一种问题
  • Docker详解
  • sh脚本发送邮件到多个收件人如何高效实现?
  • #Datawhale AI夏令营第4期#AIGC方向 文生图 Task2
  • 前端面试题整理-Javascript
  • 凤凰端子音频矩阵应用领域
  • 【问题解决】git status中文文件名乱码
  • #laravel部署安装报错loadFactoriesFrom是undefined method #
  • WordPress原创插件:Download-block-plugin下载按钮图标美化
  • 力扣面试经典算法150题:罗马数字转整数
  • SegmentFault for Android 3.0 发布
  • “Material Design”设计规范在 ComponentOne For WinForm 的全新尝试!
  • Android组件 - 收藏集 - 掘金
  • Date型的使用
  • golang 发送GET和POST示例
  • java中具有继承关系的类及其对象初始化顺序
  • k8s如何管理Pod
  • LeetCode29.两数相除 JavaScript
  • PHP 使用 Swoole - TaskWorker 实现异步操作 Mysql
  • scala基础语法(二)
  • SegmentFault 2015 Top Rank
  • unity如何实现一个固定宽度的orthagraphic相机
  • 代理模式
  • 对话:中国为什么有前途/ 写给中国的经济学
  • 函数式编程与面向对象编程[4]:Scala的类型关联Type Alias
  • 后端_MYSQL
  • 力扣(LeetCode)56
  • 如何设计一个比特币钱包服务
  • 使用iElevator.js模拟segmentfault的文章标题导航
  • 使用Tinker来调试Laravel应用程序的数据以及使用Tinker一些总结
  • 移动端解决方案学习记录
  • ‌分布式计算技术与复杂算法优化:‌现代数据处理的基石
  • # SpringBoot 如何让指定的Bean先加载
  • #职场发展#其他
  • $$$$GB2312-80区位编码表$$$$
  • (3)llvm ir转换过程
  • (4.10~4.16)
  • (51单片机)第五章-A/D和D/A工作原理-A/D
  • (C++)栈的链式存储结构(出栈、入栈、判空、遍历、销毁)(数据结构与算法)
  • (Redis使用系列) Springboot 实现Redis消息的订阅与分布 四
  • (vue)el-tabs选中最后一项后更新数据后无法展开
  • (补充)IDEA项目结构
  • (附源码)springboot高校宿舍交电费系统 毕业设计031552
  • (附源码)ssm基于jsp的在线点餐系统 毕业设计 111016
  • (接上一篇)前端弄一个变量实现点击次数在前端页面实时更新
  • (四)TensorRT | 基于 GPU 端的 Python 推理
  • ****三次握手和四次挥手
  • .NET 2.0中新增的一些TryGet,TryParse等方法
  • .net core 6 集成 elasticsearch 并 使用分词器
  • .NET DataGridView数据绑定说明