【刷题】前缀和进阶

news2024/11/18 1:45:56

在这里插入图片描述
送给大家一句话:
生活坏到一定程度就会好起来,因为它无法更坏。努力过后,才知道许多事情,坚持坚持,就过来了。 – 宫崎骏 《龙猫》

┌(˘⌣˘)ʃ┌(˘⌣˘)ʃ┌(˘⌣˘)ʃ
┌(˘⌣˘)ʃ┌(˘⌣˘)ʃ┌(˘⌣˘)ʃ
┌(˘⌣˘)ʃ┌(˘⌣˘)ʃ┌(˘⌣˘)ʃ


前缀和进阶

  • 1 前言
  • 2 Leetcode 560. 和为 K 的子数组
    • 题目描述
    • 算法思路
  • 3 Leetcode 974. 和可被 K 整除的子数组
    • 题目描述
    • 算法思路
  • 4 Leetcode 525. 连续数组
    • 题目描述
    • 算法思路
  • 5 Leetcode 1314. 矩阵区域和
    • 题目描述
  • Thanks♪(・ω・)ノ谢谢阅读!!!
  • 下一篇文章见

1 前言

今天我们继续加强对前缀和算法。
前缀和算法是对数组进行预处理操作,进而避免大量重复的操作!使得算法性能增强!
适用于对数组有大量重复操作的问题,一维预处理较简单,二维比较复杂,画图分析可以顺利解决!

2 Leetcode 560. 和为 K 的子数组

上链接:560. 和为 K 的子数组

题目描述

在这里插入图片描述
题目是好理解的,我们要在nums数组中找到满足和为K的子数组。注意这里的子数组是连续的!!!不是数学上的子数组哦!!!
来看样例:

  • 输入:nums = [1,1,1], k = 2
  • 输出:2

很明显满足条件的是[1 ,1] 和[1 , 1]。

算法思路

首先最好想的就是暴力枚举算法O(n^2):

  1. 从 0 开始依次枚举子数组的和
  2. 满足条件计数+1

这样毋庸置疑是会超时的。并且会有大量的重复操作,求了许多重复的和。那么是不是就可以进行前缀和优化呢:
我们加入预处理:

int subarraySum(vector<int>& nums, int k) {
        //sort(nums.begin() , nums.end());
        int sum[20001] = {0};
        sum[0] = 0; 
        //0 1 2 3
        for(int i = 1 ; i <= nums.size() ; i++)
            sum[i] = sum[i - 1] + nums[i - 1];
        
        int count =  0;
        // 
        for(int i = 0 ; i <= nums.size() ; i++)
        {
            for(int j = 0 ; j < i ; j++)
            {
                if(sum[i] - sum[j] == k) count++;
            }
        }
        return count;
    }

定睛一看,这这这好像时间复杂度还不如暴力算法啊,这个的时间复杂度是O(n^2) + O( n ),也会超时。那这应该怎么处理呢???

双指针(滑动窗口)可以吗?不可以,因为题目并没有说是有序数组,那么就不能保证左右指针移动方向一致!!!

我们引入一个概念:以下标为 i 结尾的子数组 。我们来分析一下

  1. 假如我们枚举到 第 i 个数字,得到了当前的前缀和 sum,
  2. 那么如果想要得到满足和为 k 的子数组,就要寻找前缀和为 sum - k的数组
  3. 那么前缀和为sum - k的数组怎么得到呢???使用哈希算法
  4. 每次的枚举都要对当前的前缀和对应的个数进行 +1
  5. 这样以后调用就方便了
    int subarraySum(vector<int>& nums, int k) {
        
        //哈希优化
        unordered_map<int , int> hash1;// 前缀和 -> 个数
        //特视情况处理
        hash1[0] = 1;
        int sum = 0; int count = 0;
        for(int i = 0; i < nums.size() ; i++)
        {
        	//记录当前前缀和
            sum += nums[i];
            //计数加上[sum - k]对应的个数
            count += hash1[sum - k];
            //把当前的前缀和统计上
            hash1[sum]++;
        }

        return count;
    }

提交看看:过啦!!!

3 Leetcode 974. 和可被 K 整除的子数组

上链接:974. 和可被 K 整除的子数组

题目描述

在这里插入图片描述
这个题目要求我们寻找 和 可以被 k 整除的子数组,很好理解。来看样例:

  • 输入:nums = [4,5,0,-2,-3,1], k = 5
  • 输出:7
  • 解释:有 7 个子数组满足其元素之和可被 k = 5 整除:
    [4, 5, 0, -2, -3, 1], [5], [5, 0], [5, 0, -2, -3], [0], [0, -2, -3], [-2, -3]

算法思路

暴力算法就不说了奥,很好想。
这道题与上一道类似,我们可以 使用以下标为 i 结尾的子数组 的方法来解决。但是如何解决判断能否整除呢???
这里要使用数学方法辅助:同余定理:
(sum - x)% k == 0 -> sum % k == x % k
这里面sum是当前的前缀和,x 是前面部分数组的前缀和,那么sum - x就可以理解为子数组的和

既然我们要寻找可以被 k 整除的子数组,就只用找到 前面的前缀和 与 当前前缀和 余数一致 的数组,就可以统计数目了:
大体框架与上道题一致
但是有一个细节需要处理 :C++余数修正
因为数据里有负数,而负数除以一个数的余数在c++中是负数,我们就要对其进行修正,并且还要保证正数的余数正确,所以就要进行一个修正:(sum % k + k) % k 这样就保证了正负数的余数都符合条件了!!!

    int subarraysDivByK(vector<int>& nums, int k) {
        //前缀和 + 哈希优化
        //unordered_map<int , int> hash;
        int hash[10001] = {0};
        hash[0] = 1;
        int count = 0;
        int sum = 0;
        //同余定理 + C++余数修正
        for(auto x : nums)
        {
            sum += x;
            //判断是否可以整除
            //(sum - x)% k == 0 -> sum % k == x % k
            count += hash[(sum % k + k) % k];
            hash[(sum % k + k) % k]++;
        }
        return count ;
    }

提交:过啦!!!

4 Leetcode 525. 连续数组

跟上节奏:525. 连续数组

题目描述

在这里插入图片描述
题目很简单,我们需要在给定的数组找到具有相同数量01的最长子数组!!!来看样例:

  • 输入: nums = [0,1,0]
  • 输出: 2
  • 说明: [0, 1] (或 [1, 0]) 是具有相同数量0和1的最长连续子数组。

算法思路

暴力算法不在多说,暴力枚举即可。
那么如何使用前缀和来解决问题呢???
我们可以将问题转换一下,把数组中的0都变成-1,然后 具有相同数量01的最长数组的和就是 0 。这样就转换为和为k的最长子数组

整体框架与Leetcode 560. 和为 K 的子数组类似,但是如何计算出最长的子数组。先前我们的哈希表储存的是前缀和 -> 个数,我们这道题使用个数肯定不行,而应该是下标位置,而且是距离最远的位置(也就是该前缀和第一次出现的位置)

    int findMaxLength(vector<int>& nums) {
        int ans = 0;
        //前缀和 + 哈希优化
        //细节处理 记录该位置的前缀和
        unordered_map<int ,int> hash1; // 前缀和 -> 最远的位置
        hash1[0] = -1;
        int sum = 0; 
        for(int i = 0 ; i < nums.size(); i++)
        {
        	//0 转换为 -1
            sum += nums[i] == 0 ? -1 : 1;
            //如果哈希表中有 sum 那么直接进行计算
            if(hash1.count(sum)) ans = max(ans , i - hash1[sum]);
            //记录的最远的位置
            else hash1[sum] = i;
            
        }
        return ans;
    }

提交:过啦!!!

5 Leetcode 1314. 矩阵区域和

家人们,上连接:1314. 矩阵区域和

题目描述

在这里插入图片描述
这道题乍一看:啥玩意儿,怎么没读懂?!
再一看:嗷嗷!原来如此!

区域和是该题的灵魂,来看样例:

  • 输入:mat = [[1,2,3],[4,5,6],[7,8,9]], k = 1
  • 输出:[[12,21,16],[27,45,33],[24,39,28]]

我们有一个3 * 3的mat矩阵,所得的ans矩阵也是 3 * 3。
ans矩阵的ans[ i ][ j ]映射到mat矩阵上是 以 mat[ i ][ j ]为中心 ,向四周扩展 k 个区域的矩阵的和。
在这里插入图片描述
当然必须保证在扩充后的区域在mat中。

那这样就简单了,就转换为二维前缀和了。我们只需要得到左上角的坐标(x1 , y1)和右下角的坐标(x2 , y2)就可以通过公式计算出区域和!!!

我们先来看二维前缀和的预处理如何来做:
在这里插入图片描述
这样需要注意一个小细节,我们进行预处理时,把dp矩阵多开一行一列可以极大的简便我们对边界情况的处理!!!

然后就是对ans矩阵进行计算,那么就要找到左上角的坐标(x1 , y1)和右下角的坐标(x2 , y2)。
为了保证不出界:

  x1 = max(0, i - k ) + 1;
  y1 = max(0, j - k ) + 1;
  x2 = min((int)ans.size() - 1, i + k ) + 1;
  y2 = min((int)ans[0].size() - 1, j + k ) + 1;

为什么要加 + 1呢?因为我们ans 矩阵没有多开一行一列,为了满足与dp满足映射关系,我们需要进行 + 1!!!

找到两个坐标后,就可以来分析计算区域和了
在这里插入图片描述

vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int k) {
    //二维前缀和
    //预处理
    vector<vector<int>> dp(mat.size() + 1, vector<int>(mat[0].size() + 1));
    for (int i = 1; i < dp.size(); i++)
    {
        for (int j = 1; j < dp[0].size(); j++)
        {
            dp[i][j] = dp[i - 1][j] + dp[i][j - 1] + mat[i - 1][j - 1] - dp[i - 1][j - 1];
        }
    }

    vector<vector<int>> ans(mat.size(), vector<int>(mat[0].size()));

    for (int i = 0; i < ans.size(); i++)
    {
        for (int j = 0; j < ans[0].size(); j++)
        {
            //寻找左上角 与 右上角 确保未出界
            int x1, x2, y1, y2;
           x1 = max(0, i - k ) + 1;
           y1 = max(0, j - k ) + 1;
           x2 = min((int)ans.size() - 1, i + k ) + 1;
           y2 = min((int)ans[0].size() - 1, j + k ) + 1;

            ans[i][j] = dp[x2][y2] - dp[x2][y1 - 1] - dp[x1 - 1][y2] + dp[x1 - 1][y1 - 1];
        }
    }
    return ans;

}

提交:过啦!!!

Thanks♪(・ω・)ノ谢谢阅读!!!

下一篇文章见

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1655895.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

三、VGA接口驱动与图像显示动态移动

文章目录 一、参数介绍二、彩条显示2.1 模块系统架构框图2.2 行、场同步波形:2.3 代码三、VGA 图像显示动态移动3.1波形设计3.2代码 一、参数介绍 对于普通的 VGA 显示器&#xff0c;共有 5 个信号&#xff1a;R、G、B 三基色&#xff1b;HS&#xff08;行同步信号&#xff09…

更专业的汽车软件研发工具链,怿星重磅发布新产品

怿星科技在2024北京国际车展同期举办主题为“创新引领未来——聚焦智能汽车软件新基建”的新产品发布会&#xff0c;重磅推出1款绝对优势产品和4套场景解决方案。同时举行了4场热点技术研讨&#xff1a;国产工具链的机遇与挑战、新架构下的的车载DDS应用探索及测试方案介绍、软…

Vue3 查看真实请求地址

上回说到Vue2查看真实请求地址&#xff0c;那么Vue3该如何查看呢&#xff1f; 传送门&#xff1a; Vue2 查看真实请求地址 1. bypass函数 使用bypass函数获取代理结果&#xff0c;设置响应头&#xff08;请求头设置未生效&#xff0c;也可以在响应头上看&#xff09;。 2. …

JVM认识之垃圾收集算法

一、标记-清除算法 1、定义 标记-清除算法是最基础的垃圾收集算法。它分为标记和清除两个阶段。先标记出所有需要回收的对象&#xff08;即垃圾&#xff09;&#xff0c;在标记完成后再统一回收所有垃圾对象。 2、优点和缺点 优点&#xff1a;实现简单缺点&#xff1a; 可能…

CCF-Csp算法能力认证, 202303-1田地丈量(C++)含解析

前言 推荐书目&#xff0c;在这里推荐那一本《算法笔记》&#xff08;胡明&#xff09;&#xff0c;需要PDF的话&#xff0c;链接如下 「链接&#xff1a;https://pan.xunlei.com/s/VNvz4BUFYqnx8kJ4BI4v1ywPA1?pwd6vdq# 提取码&#xff1a;6vdq”复制这段内容后打开手机迅雷…

Golang——IO操作

1. 输入输出的底层原理 终端其实是一个文件(Linux下一切皆文件)&#xff0c;相关实例如下&#xff1a; os.Stdin&#xff1a;标准输出的文件实例&#xff0c;类型为*Fileos.Stdout&#xff1a;标准输入的文件实例&#xff0c;类型为*Fileos.Stderr&#xff1a;标准错误输出的文…

MAC地址冲突案例

1、问题描述&#xff1a;WiFi-A网段做了策略路由&#xff0c;引流到另一台设备&#xff0c;连接WiFi-A后通过DHCP获取到了地址却无法上网&#xff0c;此时排查思路是什么&#xff1f; &#xff08;1&#xff09;、排查方法&#xff1a; 看到网关通信是否正常 第一次获取地址正…

Python——Fastapi管理平台(打包+优化)

目录 一、配置多个表 1、后端项目改造 2、导包报错——需要修改&#xff08;2个地方&#xff09; 3、启动后端&#xff08;查看是否有问题&#xff09; 4、配置前端 二、打包——成exe文件&#xff08;不包含static文件&#xff09;简单 1、后端修改 2、前端修改 3、运行打包命…

VMare Workstation安装ubuntu虚拟机异常问题处理

安装方法 ubuntu官网下载插件 异常处理 开启时报错"unable to proceed without a log file" 遇到此问题的都有一个共同点&#xff0c;工作目录路径上都带了数字&#xff0c;比如"Ubuntu 64位 01"&#xff0c;解决方法为&#xff1a; 选中"Ubuntu 64位…

mybatis-generator之一键生成:两种方法

前提使用版本为&#xff1a;jdk-1.8、mysql-8.0、maven-3.9.4 方法一 一、创建项目 二、进入pom&#xff0c;导入依赖 这里的依赖只是要用到的两个基本依赖&#xff0c;能实现功能 <?xml version"1.0" encoding"UTF-8"?> <project xmlns&qu…

数组进了多个obj,但是 在修改某个num值时,导致别的num值也发生了变化如何解决?

问题如下&#xff1a; 遇到的问题&#xff0c;数组monthArr1 push进了多个obj,但是 在修改某个num值时&#xff0c;导致别的num值也发生了变化。 而这就是深拷贝浅拷贝的问题。 解决浅拷贝使用深拷贝最简单方法 &#xff1a;JSON.parse(JSON.stringify(obj)) 或者: 使用深拷…

人工智能应用正在改变我们的生活

在这个AI蓬勃发展的时代&#xff0c;你如何使用人工智能&#xff1f;如果您认为还没有&#xff0c;请再想一想。人工智能已经为我们的许多日常活动提供了动力&#xff0c;尽管您可能还没有有意将其用作工具&#xff0c;但这种情况可能会在不久的将来发生变化。随着顶尖科技公司…

【ElementUI -- 优化小技巧系列】 -- el-tree 节点内容过长优化 以及选中默认节点

在使用elementui过程中经常碰到关于样式的问题&#xff0c;我曾经很喜欢通过类名修改css样式来做&#xff0c;其实原生封装的elementui库的样式对于普通开发来说已经足够了&#xff0c;通过类名修改css只会让组件臃肿难以维护&#xff0c;现在真的越来越怕写css&#xff0c;经常…

eBay、亚马逊、沃尔玛平台如何做测评:专业指南

在电子商务领域&#xff0c;产品测评扮演着至关重要的角色。无论是eBay、亚马逊还是沃尔玛&#xff0c;这些电商平台都依赖于用户评价和反馈来建立信任&#xff0c;推动销售&#xff0c;并优化用户体验。然而&#xff0c;如何在这些平台上进行有效的产品测评&#xff0c;对于卖…

自然语言处理(NLP)技术有哪些运用?

目录 一、自然语言处理&#xff08;NLP&#xff09;技术有哪些运用&#xff1f; 二、Python进行文本的情感分析 1、NLTK库: 2、TextBlob库: 三、错误排除 一、自然语言处理&#xff08;NLP&#xff09;技术有哪些运用&#xff1f; 自然语言处理&#xff08;NLP&#xff09…

堆的基本操作(c语言实现)

1.堆的基本操作 1.1定义堆 typedef int HPDataType;//堆中存储数据的类型typedef struct Heap {HPDataType* a;//用于存储数据的数组int size;//记录堆中已有元素个数int capacity;//记录堆的容量 }HP;1.2初始化堆 然后我们需要一个初始化函数&#xff0c;对刚创建的堆进行初…

Wappalyzer指纹识别下载安装使用教程,图文教程(超详细)

「作者简介」&#xff1a;2022年北京冬奥会网络安全中国代表队&#xff0c;CSDN Top100&#xff0c;就职奇安信多年&#xff0c;以实战工作为基础对安全知识体系进行总结与归纳&#xff0c;著作适用于快速入门的 《网络安全自学教程》&#xff0c;内容涵盖系统安全、信息收集等…

element输入框后面带输入的字符数量

使用el-input的属性&#xff1a; maxlength&#xff1a;最长字符限制&#xff1b; show-word-limit&#xff1a;显示输入字符数量&#xff1b; 例&#xff1a; js代码&#xff1a; <el-form-item label"文件名称: " prop"title"> <el-input v…

揭秘APP搭建:如何对接广告变现?

在移动应用&#xff08;App&#xff09;的开发与运营中&#xff0c;广告对接和流量变现是重要的环节。对于开发者而言&#xff0c;如何有效地利用自己的应用资源进行变现&#xff0c;同时又能提供用户良好的体验&#xff0c;是一个值得深入探讨的话题。以下便是关于App搭建如何…