LeetCode2055. 蜡烛之间的盘子

题目描述

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

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

比方说,s = “||**||**|*” ,查询 [3, 8] ,表示的是子字符串 “*||**|” 。子字符串中在两支蜡烛之间的盘子数目为 2 ,子字符串中右边两个盘子在它们左边和右边 都 至少有一支蜡烛。
请你返回一个整数数组 answer ,其中 answer[i] 是第 i 个查询的答案。
题目举例
也就是说给定一个查询数组[m,n], 计算m右侧最近的蜡烛和n左侧最近的蜡烛之间盘子的数量。

解法一:暴力解法

看到题目第一反应是暴力解法(捂脸),根据queries中的下标,每次寻找右侧最近、左侧最近的蜡烛,然后统计之间的盘子数量即可,但是肯定是超时的。但是可以通过数组来记录需要重复计算的部分,会有一点点改进,但是每次计算的话计算量还是太大。

class Solution {
public:
    vector<int> platesBetweenCandles(string s, vector<vector<int>>& queries) {
        // 查询两个杠杠之间有多少星星
        // queries中最左边的杠杠和最右边的杠杠中间有多少星星
        vector<int> res;
        // 1. 根据每个queries进行循环,但是每次都需要查询,时间复杂度n平方
        // 运行后超时
        for(auto query:queries){
            // 通过左右双指针来计算, 双指针都移到蜡烛的位置,count才有效
            int left=query[0];
            int right=query[1];
            while(s[left]=='*' || s[right]=='*'){
                if(s[left]=='*') left++;
                if(s[right]=='*') right--;
            }
            int count=0;
            for(int i=left; i<=right; i++){
                if(s[i]=='*') count++;
            }
            res.emplace_back(count);
        }
        return res;
    }
};

解法二:前缀和

前缀和是一种预处理方法,这道题中先通过一个前缀和数组prefixSum计算出每个下标处左边的盘子的数量,之后再计算两个蜡烛之间盘子的数量时,只需要右边蜡烛的前缀和减去左边蜡烛的前缀和就可以得到。 此外还需要统计原字符串中每个位置的左边最近、右边最近的蜡烛下标,这样在最终计算时,将查询数组中,比如查询数组[m, n]之间盘子的数量,就可以很方便地先找出m右侧最近的蜡烛下标,n左侧最近的蜡烛下标,这两个转化后的下标中间的盘子数量,就是m和n之间盘子的数量,然后再通过前缀和数组进行计算。

代码:
class Solution {
public:
    vector<int> platesBetweenCandles(string s, vector<vector<int>>& queries) {
        int n=s.size();
        vector<int> prefixSum(n, 0);
        // 前缀和数组
        int sum=0;
        for(int i=0; i<n; i++){
            if(s[i]=='*'){
                prefixSum[i]=++sum;
            }
            else{
                prefixSum[i]=sum;
            }
        }

        // left right两个数组
        // 统计 i位置处的字符左右最近的蜡烛
        vector<int> left(n);
        vector<int> right(n);

        // l 左蜡烛标记 检索到新蜡烛时更新
        int l=-1;
        for(int i=0; i<n; i++){
            if(s[i]=='|'){
                l=i;
                // 最近的左边蜡烛index就是自己的index
            }
            left[i]=l;
            // 如果是盘子的话,左边最近的蜡烛就是之前记录的蜡烛
        }

        // 右边蜡烛与上面基本一样
        int r=-1;
        for(int i=n-1; i>=0; i--){
            if(s[i]=='|'){
                r=i;
            }
            right[i]=r;
        }

        // prefixSum, left, right数组预计算完成
        vector<int> res;
        for(auto q:queries){
            int x=left[q[0]], y=right[q[1]];
            // 计算q中最近的左右蜡烛下标
            if(x==-1 || y==-1 || x>=y){
                // 左、右没有蜡烛 或者该位置右边最近蜡烛甚至在左边最近蜡烛左侧
                res.emplace_back(0);
            }
            else{
                res.emplace_back(prefixSum[y]-prefixSum[x]);
            }
        } 
        return res;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值