【算法文章10| 前缀和:力扣2055题:蜡烛之间的盘子】

【算法文章10| 前缀和:力扣2055题:蜡烛之间的盘子】

题目链接:2055:蜡烛之间的盘子

这道题是力扣的一道1818分的题,大概题意是这样的:

  • 给你一个字符串,字符串里面只有两种符号:字符 '*''|' ,其中 '*' 表示 盘子'|' 表示 蜡烛
  • 然后给你一个二维的查询数组queries,对于每一个查询 queries[i] = [lefti, righti],找到 子字符串中两支蜡烛之间 的盘子的 数目
  • 返回一个整数数组 answer ,其中 answer[i] 是第 i 个查询的答案。

总结一句话:找规定区间内, 在两支蜡烛之间的盘子的数目

这道题的难点在于,对于每一个查询区间[lefti, righti],怎么找区间内的离边界最近的蜡烛的位置,也就是怎么寻找“有效边界”

思路:寻找“有效边界”

  1. 定义前缀和sum[],记录盘子的数量
  2. 定义两个数组left[] 跟 right[],他们的含义如下:
    1. left[]表示:索引i左侧(含 i)最近的 ‘|’ (蜡烛) 的索引
    2. right[]表示:索引 i 右侧(含 i)最近的 ‘|’ (蜡烛) 的索引
  3. 枚举所有的查询
    • 缩小区间范围
      • 找左端点右边的第一根蜡烛,作为新的左端点
      • 找右端点左边的第一根蜡烛,作为新的右端点

代码

classSolution{publicint[]platesBetweenCandles(String s,int[][] queries){int n = s.length();//1.前缀和sum[]记录盘子的数量int[] sum =newint[n+1];//2.left[]统计索引i(含 i)左侧最近的'|'的索引int[]left =newint[n];int lastCandle =-1;for(int i =0; i < n; i++){if(s.charAt(i)=='|'){ sum[i+1]= sum[i]; lastCandle = i;}else{ sum[i+1]= sum[i]+1;} left[i]= lastCandle;}//3.right[]统计索引i(含 i)右侧最近的'|'的索引int[]right =newint[n]; lastCandle =-1;for(int i = n-1; i >=0; i--){if(s.charAt(i)=='|'){ lastCandle = i;} right[i]= lastCandle;}int[]ans =newint[queries.length];for(int i =0; i < queries.length; i++){//找左端点右边的第一根蜡烛,作为新的左端点int l = right[queries[i][0]];//找右端点左边的第一根蜡烛,作为新的右端点int r = left[queries[i][1]];//更新答案if(l !=-1&& r !=-1&& l < r){ ans[i]= sum[r]- sum[l];}else{ ans[i]=0;}}return ans;}}


一定要理解这两句代码,这两句代码非常关键,我们的目的是:left 向右移,找到区间内的第一根蜡烛。将 right 向左移,找到区间内的最后一根蜡烛。
  • 时间复杂度: O ( n + q ) O(n + q) O(n+q)
    • 遍历字符串计算前缀和 sum 和 left 数组:O(n)
    • 遍历字符串计算 right 数组:O(n)
    • 遍历查询数组 queries:O(q)(q = queries.length)

利用具体案例帮助理解代码

场景一:区间内完全没有蜡烛

假设字符串 s = "***|***",长度为 7。查询区间为 [0, 2](即前三个星号 ***)。

  1. 预处理的结果
    • right 数组:每一个位置向右看的第一根蜡烛都在索引 3。所以 right[0] = 3
    • left 数组:前三个位置向左看都没有蜡烛。所以 left[0, 1, 2] = -1
  2. 判断
    • if (l != -1 && r != -1 && l < r)
    • 这里 r 是 -1,条件直接不成立。
    • 结果ans[i] = 0
场景二:只有一根蜡烛,或蜡烛在边缘无法包裹盘子

假设字符串 s = "***|**",查询区间为 [0, 5](整个字符串)。

  1. 预处理结果
    • right[0]:从索引 0 开始向右找,第一根蜡烛在索引 3。所以 l = 3
    • left[5]:从索引 5 开始向左找,第一根蜡烛也在索引 3。所以 r = 3
  2. 判断逻辑
    • if (l < r) → \rightarrow → 即 if (3 < 3)
    • 条件不成立(因为只有一根蜡烛,无法形成“中间”区域)。
    • 结论ans[i] = 0
场景三:区间内有蜡烛,但它们中间没盘子

假设字符串 s = "**||**",查询区间为 [0, 5]

  1. 预处理结果
    • l = right[0] → \rightarrow → 索引 2(第一根 |)。
    • r = left[5] → \rightarrow → 索引 3(第二根 |)。
  2. 前缀和计算
    • sum[2](前 2 个字符里的盘子数)是 2。
    • sum[3](前 3 个字符里的盘子数)也是 2(因为索引 2 是蜡烛,盘子数没增加)。
  3. 判断
    • l < r (2 < 3) 成立。
    • ans[i] = sum[3] - sum[2] → \rightarrow →0

如果对你有帮助,欢迎点赞、关注、收藏!

Read more

【C++】AVL树(一万字超详细,看这一篇就够了!)

【C++】AVL树(一万字超详细,看这一篇就够了!)

文章目录 * AVL树的概念 * AVL树节点的定义 * AVL树的插入 * AVL树的旋转 * 左单旋-向左旋转(RR) * 右单旋-向右旋转(LL) * 左右双旋-LR * 右左双旋-RL * AVL树的验证 * AVL树的删除(了解) * AVL树的性能 * 完整代码 * * 补充 * 为什么有两处 template<class K, class V> ? * 调试小技巧 AVL树的概念 二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。 一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树: * 它的左右子树都是AVL树 * 左右子树高度之差

By Ne0inhk
【C++】速通涉及 “vector” 的经典OJ编程题

【C++】速通涉及 “vector” 的经典OJ编程题

【C++】速通涉及 “vector” 的经典OJ编程题 * 一. 杨辉三角 * 解题思路: * 代码实现: * 二. 删除有序数组中的重复项 * 解题思路: * 代码实现: * 【C/C++】按位运算符使用规制 * 三. 只出现一次的数字 * 解题思路: * 代码实现: * 四. 只出现一次的数字 III * 解题思路: * 代码实现: 一. 杨辉三角 本题LeetCode链接: 解题思路: 利用vector的特性创建一个二维数组,通过观察得知杨辉三角的0行0列全为1,其他位置元素的值都等于其上一行同列元素与上一行前一列元素的和。 代码实现: classSolution{public: vector<vector<int>>generate(int numRows){ vector<vector<int>

By Ne0inhk
C++ string 类详解:概念、常用操作与实践(算法竞赛类)

C++ string 类详解:概念、常用操作与实践(算法竞赛类)

🔥个人主页:星轨初途 ❄专栏传送门:C语言,数据结构,C++学习(竞赛类)算法及编程题分享 文章目录 * 前言 * 一、string概念 * 二、string的常见操作和功能 * 1、头文件 * 2、创建字符串 * 3、string字符串的输入 * (1)正常输入(cin) * (2)getline(带空格输入) * 第一种(默认以‘\n’为结束标志) * 第二种(自定义结束标志) * 4、size()——字符串长度 * 5、迭代器(iterator) * begin()和end() * (1)比较 * (2)遍历 * 改变指定字符 * 6、字符串的插入和删除 * (1)插入

By Ne0inhk