题目描述
给你一个长桌子,桌子上盘子和蜡烛排成一列。给你一个下标从 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;
}
};