leetcode2055. 蜡烛之间的盘子 - 前缀和

news/2024/12/16 18:12:40/文章来源:https://www.cnblogs.com/CatCatcher/p/18610844

这道题目作为比较单纯的前缀和题目,不需要额外的一些知识,只需要了解前缀和数组的生成与使用即可,并且也有一定的难度(难度分1819),是一个比较好的前缀和例题。

题干

算术评级: 6第 64 场双周赛Q3

给你一个长桌子,桌子上盘子和蜡烛排成一列。给你一个下标从 0 开始的字符串 s ,它只包含字符 '' 和 '|' ,其中 '' 表示一个 盘子 ,'|' 表示一支 蜡烛 。

同时给你一个下标从 0 开始的二维整数数组 queries ,其中 queries[i] = [lefti, righti] 表示 子字符串 s[lefti...righti] (包含左右端点的字符)。对于每个查询,你需要找到 子字符串中 在 两支蜡烛之间 的盘子的 数目 。如果一个盘子在 子字符串中 左边和右边 都 至少有一支蜡烛,那么这个盘子满足在 两支蜡烛之间 。

比方说,s = "|||||" ,查询 [3, 8] ,表示的是子字符串 "||**|" 。子字符串中在两支蜡烛之间的盘子数目为 2 ,子字符串中右边两个盘子在它们左边和右边 都 至少有一支蜡烛。
请你返回一个整数数组 answer ,其中 answer[i] 是第 i 个查询的答案。

示例 1:
image

输入:s = "||***|", queries = [[2,5],[5,9]]
输出:[2,3]
解释:

  • queries[0] 有两个盘子在蜡烛之间。
  • queries[1] 有三个盘子在蜡烛之间。
    示例 2:
    image

输入:s = "||||||*", queries = [[1,17],[4,5],[14,17],[5,11],[15,16]]
输出:[9,0,0,0,0]
解释:

  • queries[0] 有 9 个盘子在蜡烛之间。
  • 另一个查询没有盘子在蜡烛之间。

提示:

3 <= s.length <= 105
s 只包含字符 '*' 和 '|' 。
1 <= queries.length <= 105
queries[i].length == 2
0 <= lefti <= righti < s.length

思路

假设有一个例子字符串s = '**|***|*',需要查询q1q2有多少盘子在两个蜡烛之间
image

那么我们只需要知道:

  1. q1q2总共有多少盘子totalPlants
  2. q1开始从左往右数,第一个蜡烛的位置p1
  3. q2开始从右往左数,第一个蜡烛的位置p2

之后使用totalPlants - (p1 - q1) - (q2 - p2),剔除不在蜡烛包围内的盘子数,就可以得到被蜡烛包围的盘子数。

编码思路

  1. 如何得到totalPlants
    使用前缀和统计得到前缀和数组suffixPlate,使用suffixPlate[q2] - (suffixPlate[q1 - 1] ?? 0)即可得到q1q2之间的盘子数量
  2. 如何得到p1p2的位置?
    • 对于p2,可以使用一个数组lFirstCandle,数组长度与字符串s的长度一样,lFirstCandle[i]表示s[i]的左边最近的蜡烛的位置。这样我们只需要查询lFirstCandle[q2],就能得到p2的值了
    • 对于p1,可以使用一个数组rFirstCandle,数组长度与字符串s的长度一样,rFirstCandle[i]表示s[i]右边最近的蜡烛的位置。这样我们只需要查询lFirstCandle[q1],就能得到p1的值了
  3. 边界情况:q1q2之间只有一根蜡烛或者没有蜡烛
    • q1q2之间只有一根蜡烛会使得totalPlants - (p1 - q1) - (q2 - p2)结果为0,直接返回0即可
    • q1q2之间没有蜡烛会使得totalPlants - (p1 - q1) - (q2 - p2)的结果为负数,此时返回0即可

代码&gpt分析

function platesBetweenCandles(s: string, queries: number[][]): number[] {let lFirstCandle: number[] = [];let rFirstCandle: number[] = [];let prefixPlate: number[] = [];// 构建前缀和数组for (const char of s) prefixPlate.push((prefixPlate.at(-1) ?? 0) + (char === "*" ? 1 : 0));let l = 0;let lCandlePosition = -1;let r = s.length - 1;let rCandlePosition = s.length;// 缓存左右侧最近的蜡烛的下标while (s[l]) {if (s[l] === "|") lCandlePosition = l;if (s[r] === "|") rCandlePosition = r;lFirstCandle.push(lCandlePosition);rFirstCandle[r] = rCandlePosition;l++;r--;}return queries.map(([l, r]) => {let totalPlants = prefixPlate[r] - (prefixPlate[l - 1] ?? 0);// 考虑负数情况下的边界情况return Math.max(0, totalPlants - (r - lFirstCandle[r]) - (rFirstCandle[l] - l));});
}

算法逻辑

该算法的核心目的是在一个由蜡烛 (|) 和盘子 (*) 组成的字符串中,根据给定的查询,快速计算两根蜡烛之间盘子的数量。通过预处理构建前缀和数组和左右最近蜡烛位置数组,将每次查询的复杂度从 O(n)O(n)O(n) 降低到 O(1)O(1)O(1)。


变量解释

  1. lFirstCandle: 数组,用于存储每个字符左侧最近的蜡烛的索引。如果当前位置没有蜡烛,则存储上一个蜡烛的位置。
  2. rFirstCandle: 数组,用于存储每个字符右侧最近的蜡烛的索引。如果当前位置没有蜡烛,则存储下一个蜡烛的位置。
  3. prefixPlate: 前缀和数组,用于存储从字符串开头到当前位置盘子 (*) 的累计数量。
  4. lCandlePosition: 临时变量,用于记录遍历过程中左侧最近的蜡烛的位置。
  5. rCandlePosition: 临时变量,用于记录遍历过程中右侧最近的蜡烛的位置。
  6. lr: 指针变量,分别从左向右和从右向左遍历字符串。

关键代码解析

  1. 构建前缀和数组

    javascript复制代码
    for (const char of s) prefixPlate.push((prefixPlate.at(-1) ?? 0) + (char === "*" ? 1 : 0));
    

    这段代码通过判断每个字符是否是盘子 (*),计算当前累计的盘子数量,并存储到前缀和数组中。如果字符是蜡烛 (|),前缀和保持不变。

  2. 缓存左右侧最近蜡烛的下标

    javascript复制代码
    while (s[l]) {if (s[l] === "|") lCandlePosition = l;if (s[r] === "|") rCandlePosition = r;lFirstCandle.push(lCandlePosition);rFirstCandle[r] = rCandlePosition;l++;r--;
    }
    

    双指针分别从字符串两端向中间遍历,记录每个字符左侧和右侧最近的蜡烛位置,并将其存储到 lFirstCandlerFirstCandle 数组中。

  3. 处理每个查询

    javascript复制代码
    return queries.map(([l, r]) => {let totalPlants = prefixPlate[r] - (prefixPlate[l - 1] ?? 0);return Math.max(0, totalPlants - (r - lFirstCandle[r]) - (rFirstCandle[l] - l));
    });
    
    • 通过前缀和数组计算区间内盘子的总数。
    • 减去两端无法被两根蜡烛包围的盘子数量。
    • 使用 Math.max(0, ...) 确保结果非负。

性能分析

  1. 时间复杂度
    • 预处理阶段(前缀和数组和左右最近蜡烛数组):O(n)O(n)O(n),其中 nnn 是字符串长度。
    • 查询阶段:每次查询是 O(1)O(1)O(1),总查询复杂度是 O(q)O(q)O(q),其中 qqq 是查询的数量。
    • 总体时间复杂度:O(n+q)O(n + q)O(n+q)。
  2. 空间复杂度
    • 使用了 prefixPlatelFirstCandlerFirstCandle,每个数组的大小为 O(n)O(n)O(n)。
    • 总空间复杂度为 O(n)O(n)O(n)。

适用场景

  1. 字符串区间统计问题:需要在多次查询中高效统计特定字符的数量。

  2. 动态规划问题优化:通过预处理构建前缀和数组或最近位置数组来降低查询复杂度。

  3. 场景示例

    • 盘子和蜡烛的场景可用于描述线性排列中的区间统计问题,如铁路站点与列车车厢统计。
    • DNA 序列中某两段标记之间核苷酸的计数问题。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

云原生周刊:Kubernetes v1.32 正式发布

云原生周刊:Kubernetes v1.32 正式发布 开源项目推荐 Helmper Helmper 简化了将 Helm Charts导入OCI(开放容器倡议)注册表的过程,并支持可选的漏洞修复功能。它确保您的 Helm Charts不仅安全存储,还能及时应用最新的安全修复。该工具完全兼容 OCI 标准,能够方便地与 OCI …

五款实用报表工具推荐:助你轻松搞定数据可视化!

概述 本文将为大家介绍五款功能各异的报表工具,包括山海鲸报表、Canva、BIRT、Chart.js和Metabase。文章详细分析了每款工具的特点、优缺点以及适用场景,帮助企业用户根据自身需求选择合适的报表解决方案,以提高数据分析和可视化能力。 1. 山海鲸报表 简介山海鲸报表是一款国…

三、USB PD物理层

1、定义 物理层(PHY层)定义了USB电源传输的信令技术。本章定义了USB PD设备之间的互操作性所需的PD物理层的电气要求和参数。 2、物理层的功能 USB PD物理层由一对发射机和接收器组成,它们通过单个信号线(CC)进行通信。所有的通信都是半双工的。PHY层实现了避免冲突,以最小…

FineReport模板性能问题排查方法

1. 概述 模板的加载速度受到很多因素影响,如果一个模板预览的时候,加载较慢,该如何去分析问题原因呢? 2. 排查步骤 2.1 查看数据集查询速度 大部分模板加载慢,都是因为 sql 执行速度比较慢。那么如何验证 sql 速度快慢呢?可以使用以下几种方案。 1)在设计器的数据集中直…

模板性能问题排查方法

1. 概述 模板的加载速度受到很多因素影响,如果一个模板预览的时候,加载较慢,该如何去分析问题原因呢? 2. 排查步骤 2.1 查看数据集查询速度 大部分模板加载慢,都是因为 sql 执行速度比较慢。那么如何验证 sql 速度快慢呢?可以使用以下几种方案。 1)在设计器的数据集中直…

【bug】重复请求的幂等问题

问题背景 某验收系统,客户发起验收流程时,由于前端没有做防重点击的限制,导致申请按钮连续点击了多次,重复发起了多条流程 历史逻辑 后端为了保证接口幂等,在发起验收流程的代码中加了几层逻辑如下:判断验收记录状态是否为待发起, 如果不是,则立刻返回失败 发起流程的入…

Deformable DETR

PDF:https://arxiv.org/pdf/2010.04159 Code:https://github.com/fundamentalvision/Deformable-DETR 一、大体内容 前面介绍DETR时,说明了其还存在对小物体检测效果不佳和训练慢的问题,Deformable DETR引入了DCN(Deformable Convolutional Networks)并将其和DETR相结合,借…

【蓝队】HW中盛行的Java内存马,如何全面检测?

一、背景 1.1 Java内存马是什么? 内存马是一种仅在内存中运行、没有文件落地的恶意程序,因此具有较强的隐蔽性,能够避开常规的基于文件系统的检测。Java内存马是针对Java语言的内存马,它利用Java语言的动态特性,如类加载机制、动态代理和反射技术等,在Java应用的内存中注…

Express的使用笔记9 使用bcrypt算法给用户密码加密

先了解一下bcrypt算法,一种基于Blowfish密码学算法的密码散列函数,用于在密码存储时抵抗暴力破解攻击,通过在散列过程中加salt来提高安全性,salt是个随机生成的数据串,与密码一起被散列,使得即使两个相同的密码也会产生不同的散列值。bcrypt算法允许开发者指定工作因子(…

4.mysql中的存储过程

创建存储过程和函数 CREATE[DEFINER = {USER | CURRENT_USER}] # 定义者是谁PROCEDURE sp_name ([proc_parameter[,...]])[characteristic ...] routine_bodyCREATE [DEFINER = {USER | CURRENT_USER}]FUNCTION sp_name ([function_name[,...]])[characteristic ...] routine_…

印象笔记使用

vscode安装插件登录印象笔记,开通token点击插件页面的设置填写url和tokenctrl + shift + pever new - 新建笔记 ever open - 打开笔记 ever search - 搜索笔记 ever publish - 发布笔记 ever sync - 同步笔记新建笔记下载安装windows客户端,功能比网页端更全面客户端才能导出…

(BIBM-2024) 用于药物相互作用预测的可解释多视图注意网络

用于药物相互作用预测的可解释多视图注意网络 论文标题: Interpretable multi-view attention network for drug-drug interaction prediction 论文地址: https://ieeexplore.ieee.org/document/10385757 论文期刊: BIBM 2024 摘要 药物间相互作用(DDI)在药物发现中发挥着越来…