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] 。 没有其他日子符合这个条件,所以日子 2 和 3 是适合打劫银行的日子。
思路:
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; } };