LeetCode Daily 32

2022-3-8 T.2055 蜡烛之间的盘子

 

好久没更了,来连更两道前缀和的题 

 

题目描述:

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

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

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

 

示例:

输入:s = "**|**|***|", queries = [[2,5],[5,9]]
输出:[2,3]
解释:
- queries[0] 有两个盘子在蜡烛之间。
- queries[1] 有三个盘子在蜡烛之间。

 

思路:

开始时思路明确,暴力求解,遇到两个蜡烛则计算之间盘子个数,发现超时。

 

class Solution {
public:
    int cac(string s, vector<int> num) {
        int candle = 0, plate = 0, ans = 0, left = num[0], right = num[1];
        for(int i = left; i <= right; i++){
            if(s[i] == '|') candle++;
            if(candle != 0 && s[i] == '*' && candle != 2) plate++;
            if(candle == 2){
                ans = max(ans, plate);
                candle = 1;
            }
        }
        return ans;
    }
    
    vector<int> platesBetweenCandles(string s, vector<vector<int>>& queries) {
        vector<int> ans;
        for(int i = 0; i < queries.size(); i++){
            ans.push_back(cac(s, queries[i]));
        }
        return ans;
    }
};

 

根据题解思路得知,本题采用前缀和的解法求解。

preSum数组计算出各位置上之前的盘子数目。left数组计算出每个位置最左的蜡烛位置,right数组计算出每个位置最右边的蜡烛位置。

进行遍历,找出给定区间内最左最右的蜡烛位置,进行判断,若不存在,或最左位大于最右位则之间无盘子,否则即前缀和之差为最左最右蜡烛间盘子数目。

 

class Solution {
public:
    vector<int> platesBetweenCandles(string s, vector<vector<int>>& queries) {
        vector<int> preSum(s.size());
        vector<int> left(s.size());
        vector<int> right(s.size());
        vector<int> ans;
        for(int i = 0, sum = 0; i < s.size(); i++){
            if(s[i] == '*') sum++;
            preSum[i] = sum;
        }
        for(int i = 0, leftCandle = -1; i < s.size(); i++){
            if(s[i] == '|') leftCandle = i;
            left[i] = leftCandle;
        }
        for(int i = s.size() - 1, rightCandle = -1; i >= 0; i--){
            if(s[i] == '|') rightCandle = i;
            right[i] = rightCandle;
        }
        for(auto& query : queries){
            int first = right[query[0]], second = left[query[1]];
            ans.push_back(first == -1 || second == -1 || first >= second ? 0 : preSum[second] - preSum[first]);
        }
        return ans;
    }
};

 

T.2100 适合打劫银行的日子

 

本题与上一题大同小异,都是利用前缀和进行分析求解(有点dp的味道)

 

题目描述:

你和一群强盗准备打劫银行。给你一个下标从 0 开始的整数数组 security ,其中 security[i] 是第 i 天执勤警卫的数量。日子从 0 开始编号。同时给你一个整数 time 。

如果第 i 天满足以下所有条件,我们称它为一个适合打劫银行的日子:

第 i 天前和后都分别至少有 time 天。
第 i 天前连续 time 天警卫数目都是非递增的。
第 i 天后连续 time 天警卫数目都是非递减的。
更正式的,第 i 天是一个合适打劫银行的日子当且仅当:security[i - time] >= security[i - time + 1] >= ... >= security[i] <= ... <= security[i + time - 1] <= security[i + time].

请你返回一个数组,包含 所有 适合打劫银行的日子(下标从 0 开始)。返回的日子可以 任意 顺序排列。

 

示例:

输入:security = [5,3,3,3,5,6,2], time = 2
输出:[2,3]
解释:
第 2 天,我们有 security[0] >= security[1] >= security[2] <= security[3] <= security[4] 。
第 3 天,我们有 security[1] >= security[2] >= security[3] <= security[4] <= security[5] 。
没有其他日子符合这个条件,所以日子 23 是适合打劫银行的日子。

 

思路:

left数组找出抢劫日前满足的天数,right数组找出抢劫日后满足的天数。

判断后则i即为抢劫日。

 

代码:

class Solution {
public:
    vector<int> goodDaysToRobBank(vector<int>& security, int time) {
        vector<int> left(security.size());
        vector<int> right(security.size());
        vector<int> ans;
        for(int i = 1; i < security.size(); i++)
            if(security[i - 1] >= security[i]) left[i] = left[i - 1] + 1;
        for(int i = security.size() - 2; i >= 0; i--)
            if(security[i + 1] >= security[i]) right[i] = right[i + 1] + 1;
        for(int i = 0; i < security.size(); i++)
            if(left[i] >= time && right[i] >= time) ans.push_back(i);
        return ans;
    }
};

 

posted @ 2022-03-08 17:31  HM-7  阅读(33)  评论(0)    收藏  举报