优选算法之前缀和(下)

news2024/11/24 19:18:04

目录

一、和为 k 的子数组

1.题目链接:560. 和为 K 的子数组

2.题目描述:

3.解法(前缀和 + 哈希表)

🌻算法思路:

🌻算法代码:

二、和可被 k 整除的子数组

1.题目链接:974. 和可被 K 整除的子数组

2.题目描述:

3.解法(前缀和 + 哈希表)

🌻本题需要的前置知识:

🌻算法思路:

🌻算法代码:

三、连续数组

1.题目链接:525. 连续数组

2.题目描述:

3.解法(前缀和在哈希表中)

🌻算法思路:

🌻算法代码:

四、矩阵区域和

1.题目链接:1314. 矩阵区域和

2.题目描述:

3.解法(前缀和在哈希表中)

🌻算法思路:

🌻算法代码:


一、和为 k 的子数组

1.题目链接:560. 和为 K 的子数组

2.题目描述:

给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 

子数组是数组中元素的连续非空序列。

示例 1:

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

示例 2:

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

3.解法(前缀和 + 哈希表

🌻算法思路:

设 i 为数组中的任意位置,用 sum[i] 表示 [0, i] 区间内所有元素的和。

想知道有多少个「以 i 为结尾的和为 k 的子数组」,就要找到有多少个起始位置为 x1, x2, x3... 使得 [x, i] 区间内的所有元素的和为 k 。那么 [0, x] 区间内的和就是 sum[i] - k 了。于是问题就变成:

  • 找到在 [0, i - 1] 区间内,有多少前缀和等于 sum[i] - k 的即可。

我们不用真的初始化一个前缀和数组,因为我们只关心在 i 位置之前,有多少个前缀和等于 sum[i] - k 。因此,我们仅需用一个哈希表,一边求当前位置的前缀和,一边存下之前每一种前缀和出现的次数。

🌻算法代码:

class Solution 
{
public:
    int subarraySum(vector<int>& nums, int k) 
    {
        unordered_map<int, int> hash;// 统计前缀和出现的次数
        hash[0] = { 1 };

        int sum = 0, ret = 0;
        for(auto x : nums)
        {
            sum += x;// 计算当前位置的前缀和
            if(hash.count(sum - k))
                ret += hash[sum - k];// 统计个数
            hash[sum]++;
        }
        return ret;
    }
};

二、和可被 k 整除的子数组

1.题目链接:974. 和可被 K 整除的子数组

2.题目描述:

给定一个整数数组 nums 和一个整数 k ,返回其中元素之和可被 k 整除的非空 子数组 的数目。

子数组 是数组中 连续 的部分。

示例 1:

输入: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]

示例 2:

输入: nums = [5], k = 9
输出: 0

3.解法(前缀和 + 哈希表

🌻本题需要的前置知识:

  • 同余定理

如果 (a - b) % n == 0 ,那么我们可以得到一个结论: a % n == b % n 。用文字叙述就是,如果两个数相减的差能被 n 整除,那么这两个数对 n 取模的结果相同。

例如: (26 - 2) % 12 == 0 ,那么 26 % 12 == 2 % 12 == 2 。

  • c++ 中负数取模的结果,以及如何修正「负数取模」的结果

a. c++ 中关于负数的取模运算,结果是「把负数当成正数,取模之后的结果加上一个负号」。

例如: -1 % 3 = -(1 % 3) = -1

b. 因为有负数,为了防止发生「出现负数」的结果,以 (a % n + n) % n 的形式输出保证为正。

例如: -1 % 3 = (-1 % 3 + 3) % 3 = 2

🌻算法思路:

设 i 为数组中的任意位置,用 sum[i] 表示 [0, i] 区间内所有元素的和。

  • 想知道有多少个「以 i 为结尾的可被 k 整除的子数组」,就要找到有多少个起始位置为 x1,x2, x3... 使得 [x, i] 区间内的所有元素的和可被 k 整除。
  • 设 [0, x - 1] 区间内所有元素之和等于 a , [0, i] 区间内所有元素的和等于 b ,可得(b - a) % k == 0 。
  • 由同余定理可得, [0, x - 1] 区间与 [0, i] 区间内的前缀和同余。于是问题就变成:找到在 [0, i - 1] 区间内,有多少前缀和的余数等于 sum[i] % k 的即可。

我们不用真的初始化一个前缀和数组,因为我们只关心在 i 位置之前,有多少个前缀和等于sum[i] - k 。因此,我们仅需用⼀个哈希表,⼀边求当前位置的前缀和,⼀边存下之前每⼀种前缀和出现的次数。

🌻算法代码:

class Solution 
{
public:
    int subarraysDivByK(vector<int>& nums, int k) 
    {
        unordered_map<int, int> hash;
        hash[0 % k] = { 1 };// 0这个数的余数

        int sum = 0, ret = 0;
        for(auto x : nums)
        {
            sum += x;// 算出当前位置的前缀和
            int r = (sum % k + k) % k;// 修正后的余数
            if(hash.count(r))
                ret += hash[r];// 统计结果
            hash[r]++;
        }
        return ret;
    }
};

三、连续数组

1.题目链接:525. 连续数组

2.题目描述:

给定一个二进制数组 nums , 找到含有相同数量的 0 和 1 的最长连续子数组,并返回该子数组的长度。

示例 1:

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

示例 2:

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

3.解法(前缀和在哈希表中

🌻算法思路:

稍微转化⼀下题目,就会变成我们熟悉的题:

  • 本题让我们找出⼀段连续的区间, 0 和 1 出现的次数相同。
  • 如果将 0 记为 -1 , 1 记为 1 ,问题就变成了找出⼀段区间,这段区间的和等于 0 。
  • 于是,就和 560. 和为 K 的⼦数组 这道题的思路⼀样。

设 i 为数组中的任意位置,用 sum[i] 表示 [0, i] 区间内所有元素的和。

想知道最大的「以 i 为结尾的和为 0 的子数组」,就要找到从左往右第⼀个 x1 使得 [x1, i] 区间内的所有元素的和为 0 。那么 [0, x1 - 1] 区间内的和是不是就是 sum[i] 了。于是问题就变成:

  • 找到在 [0, i - 1] 区间内,第⼀次出现 sum[i] 的位置即可。

我们不用真的初始化⼀个前缀和数组,因为我们只关心在 i 位置之前,第⼀个前缀和等于 sum[i]的位置。因此,我们仅需用⼀个哈希表,⼀边求当前位置的前缀和,⼀边记录第⼀次出现该前缀和的位置。

🌻算法代码:

class Solution 
{
public:
    int findMaxLength(vector<int>& nums) 
    {
        unordered_map<int, int> hash;
        hash[0] = -1;// 默认有一个前缀和为 0 的情况
        
        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;
    }
};

四、矩阵区域和

1.题目链接:1314. 矩阵区域和

2.题目描述:

给你一个 m x n 的矩阵 mat 和一个整数 k ,请你返回一个矩阵 answer ,其中每个 answer[i][j] 是所有满足下述条件的元素 mat[r][c] 的和: 

  • i - k <= r <= i + k,
  • j - k <= c <= j + k 且
  • (r, c) 在矩阵内。

示例 1:

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

示例 2:

输入:mat = [[1,2,3],[4,5,6],[7,8,9]], k = 2
输出:[[45,45,45],[45,45,45],[45,45,45]]

3.解法(前缀和在哈希表中

🌻算法思路:

  1. 二维前缀和的简单应用题,关键就是我们在填写结果矩阵的时候,要找到原矩阵对应区域的「左上角」以及「右下角」的坐标(推荐画图)。
  2. 左上角坐标: x1 = i - k,y1 = j - k ,但是由于会「超过矩阵」的范围,因此需要对 0 取⼀个 max 。因此修正后的坐标为: x1 = max(0, i - k), y1 = max(0, j - k) ;
  3. 右下角坐标: x1 = i + k,y1 = j + k ,但是由于会「超过矩阵」的范围,因此需要对m - 1,以及 n - 1 取一个min。因此修正后的坐标为:x2 = min(m - 1, y + k), y2 = min(n - 1, j + k)。
  4. 然后将求出来的坐标代入到「二维前缀和矩阵」的计算公式上即可~(但是要注意下标的映射关系)。

🌻算法代码:

class Solution 
{
public:
    vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int k) 
    {
        int m = mat.size(), n = mat[0].size();
        // 1.预处理前缀和矩阵
        vector<vector<int>> dp(m + 1, vector<int>(n + 1));
        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] + mat[i - 1][j - 1] - dp[i - 1][j - 1];
            }
        }

        // 2.使用
        vector<vector<int>> ret(m, vector<int>(n));
        for(int i = 0; i < m; i++)
        {
            for(int j = 0; j < n; j++)
            {
                int x1 = max(0, i - k) + 1, y1 = max(0, j - k) + 1;
                int x2 = min(m - 1, i + k) + 1, y2 = min(n - 1, j + k) + 1;
                ret[i][j] = dp[x2][y2] - dp[x1 - 1][y2] - dp[x2][y1 - 1] + dp[x1 - 1][y1 - 1];
            }
        }
        return ret;
    }
};

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

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

相关文章

MySQL中多表查询之外连接

首先先来介绍一下我做的两个表&#xff0c;然后再用他们两个举例说明。 -- 创建教师表 create table teachers( id_t int primary key auto_increment, -- 老师编号 name_t varchar(5) -- 姓名 ); -- 创建学生表 create table students( id_s int primary key auto_increment,…

Android APK混淆处理方案分析

这里写目录标题 一、前言1.1 相关工具二、Apk 分析2.1 apk 解压文件2.2 apk 签名信息2.3 apk AndroidManifest.xml2.4 apk code三、Apk 处理3.1 添加垃圾文件3.2 AndroidManifest.xml 处理3.3 dex 混淆处理3.4 zipalign对齐3.5 apk 重新签名3.6 apk 安装测试四、总结一、前言 提…

使用Astro+Vercel+Cloudflare一天时间开发部署上线一个知识博客网站,简直简简单单

大家好&#xff0c;这里是程序猿代码之路。在当今数字化时代&#xff0c;拥有一个个人博客网站对于分享知识、展示个人品牌变得越来越重要。然而&#xff0c;许多非技术背景的用户对于搭建和维护一个网站可能会感到望而却步。幸运的是&#xff0c;随着低代码和无代码平台的兴起…

Spring高手之路21——深入剖析Spring AOP代理对象的创建

文章目录 创建代理对象核心动作的三个步骤1. 判断 Bean 是否需要增强&#xff08;源码分析时序图说明&#xff09;2. 匹配增强器 Advisors&#xff08;源码分析时序图说明&#xff09;3. 创建代理对象&#xff08;源码分析时序图说明&#xff09; 创建代理对象核心动作的三个步…

C++模版基础知识与STL基本介绍

目录 一. 泛型编程 二. 函数模板 1. 概念 2. 函数模版格式 3. 函数模版的原理 4. 模版函数的实例化 (1). 隐式实例化 (2.) 显式实例化 5. 模版参数的匹配原则 三. 类模板 1. 类模板的定义格式 2. 类模板的实例化 四. STL的介绍 1. 什么是STL&#xff1f; 2. STL的版…

netty入门-7 ByteBuf

文章目录 前言ByteBuf结构池化与非池化创建&#xff08;直接内存/堆内存&#xff09;写入和读取释放零拷贝&#xff0c;slice&#xff0c;duplicate&#xff0c; copy&#xff0c;Composite 结语 前言 ByteBuf这部分视频讲的更为详细。 主要参考视频。 ByteBuf 结构 首先这…

C# Task.WaitAll 的用法

目录 简介 1.WaitAll(Task[], Int32, CancellationToken) 2.WaitAll(Task[]) 3.WaitAll(Task[], Int32) 4.WaitAll(Task[], CancellationToken) 5.WaitAll(Task[], TimeSpan) 结束 简介 Task.WaitAll 是 C# 中用于并行编程的一个的方法&#xff0c;它属于 System.Threa…

开始尝试从0写一个项目--后端(三)

器材管理 和员工管理基本一致&#xff0c;就不赘述&#xff0c;展示代码为主 新增器材 表设计&#xff1a; 字段名 数据类型 说明 备注 id bigint 主键 自增 name varchar(32) 器材名字 img varchar(255) 图片 number BIGINT 器材数量 comment VARC…

Elasticsearch 使用误区之三——分片设置不合理

Elasticsearch 是一个强大的搜索和分析引擎&#xff0c;它通过将数据分散到多个节点的分片中来进行分布式处理。 本文将探讨分片大小和策略的概念&#xff0c;以优化 Elasticsearch 的性能并防止过度分片或分片过大等问题。 先看个分片设置不合理的真实企业案例&#xff1a; 10…

陶晶驰串口屏使用记录与教程

首先把串口屏想象成和正点原子usmart调试程序一样的程序&#xff0c;串口屏主芯片有些是GD32 STM32都是主流单片机&#xff0c;里面下载了一些固件形成了现在的操作系统 其实我更喜欢把他们&#xff08;usmart&#xff0c;串口屏主程序&#xff0c;micropython&#xff0c;at指…

uniapp vue3 使用画布分享或者收藏功能

使用HBuilder X 开发小程序&#xff0c;大多数的画布插件很多都是vue2的写法&#xff0c;vue3的很少 我自己也试了很多个插件&#xff0c;但是有一些还是有问题&#xff0c;不好用 海报画板 - DCloud 插件市场 先将插件导入项目中 自己项目亲自用过&#xff0c;功能基本是完善…

GraphRAG:基于实体的本地搜索方法:知识图谱与非结构化数据的融合

GraphRAG&#xff1a;基于实体的本地搜索方法:知识图谱与非结构化数据的融合 在自然语言处理和信息检索领域,如何有效地结合结构化知识和非结构化文本数据一直是一个重要的研究方向。本文介绍一种基于实体的本地搜索方法,该方法巧妙地融合了知识图谱中的结构化数据和输入文档中…

优化冗余代码:提升前端项目开发效率的实用方法

目录 前言代码复用与组件化模块化开发与代码分割工具辅助与自动化结束语 前言 在前端开发中&#xff0c;我们常常会遇到代码冗余的问题&#xff0c;这不仅增加了代码量&#xff0c;还影响了项目的可维护性和开发效率。还有就是有时候会接到紧急业务需求&#xff0c;要求立马完…

打造一篇完美的【数学建模竞赛论文】:从准备到撰写的全面指南

目录 一、赛前准备 1.1 报名与纪律要求 1.2 MD5码上传 1.3 竞赛准备 1.4 时间分配 二、论文格式规范 2.1 摘要 2.2 参考文献 2.3 排版要求 三、建模过程与方法 3.1 问题分析与模型假设 3.2 模型构建与求解 3.3 结果分析与检验 四、论文撰写技巧 4.1 论文结构 4…

Redisson中分布式锁继承体系

直接上图 画了好久 关于非公平锁和公平锁中差异化函数如tryLockInnerAsyc 和unsubscribe还没有时间进行探索&#xff0c;这应该是公平锁和非公平锁之间的差异所在。 说一说Redisson中的类之间关系设计 参考抽象类实现接口_一个抽象之类 如果要实现某个接口怎么办-CSDN博客 众…

电脑文件误删除如何恢复?数据恢复第一步是什么?这五点要第一时间处理!

电脑文件误删除如何恢复&#xff1f;数据删除恢复的第一时间要做什么&#xff0c;你知道吗&#xff1f; 在使用电脑的过程中&#xff0c;误删除重要文件的情况时有发生。面对这种情况&#xff0c;不必过于慌张&#xff0c;因为有多种方法可以帮助你恢复误删除的文件。以下是恢复…

金字塔监督在人脸反欺骗中的应用

介绍 论文地址&#xff1a;https://arxiv.org/pdf/2011.12032.pdf 近年来&#xff0c;人脸识别技术越来越普及。在智能手机解锁和进出机场时&#xff0c;理所当然地会用到它。人脸识别也有望被用于管理今年奥运会的相关人员。但与此同时&#xff0c;人们对人脸欺骗的关注度也…

醒醒,别睡了...讲《数据分析pandas库》了—/—<3>

直接上知识点 一、 1、新建数据框时建立索引 所有的数据框默认都已经使用从 0 开始的自然数索引&#xff0c;因此这里的"建立”索引指的是自定 df pd.DataFrame( {varl : 1.0, var2 :[1,2,3,4], var3 :[test,python,test,hello] , var4 : cons} , index [0,1,2,3]) …

【ESP32 IDF SPI硬件驱动W25Q64】

目录 SPISPI介绍idf配置初始化配置通信 驱动代码 SPI SPI介绍 详细SPI介绍内容参考我之前写的内容【ESP32 IDF 软件模拟SPI驱动 W25Q64存储与读取数组】 idf配置 初始化配置 spi_bus_initialize() 参数1 &#xff1a;spi几&#xff0c;例如spi2,spi3 参数2&#xff1a;…

MySQL体系结构与查询执行流程详解

MySQL 体系结构与查询执行过程详解 MySQL 是一个采用单进程多线程架构模式的关系型数据库管理系统。本文将详细介绍 MySQL 的体系结构及其查询语句的执行过程,并探讨性能优化的关键点。 MySQL 体系结构 MySQL 的架构为 Client-Server 架构。总体上,我们可以将 MySQL 的体系…