持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第22天,点击查看活动详情
LeetCode每日一题打卡专栏正式启动!不出意外将日更LeetCode的每日一题,敬请期待。
2055:蜡烛之间的盘子
题意:
给定一个字符串数组,含有*和|两个字符,分别表示盘子和蜡烛。现给出一个询问数组,每个元素给定一个区间[l,r],需要求得该区间中两支蜡烛之间的最大盘子数量
其中:
示例1:
输入:s = `"**|**|***|"`, queries = [[2,5],[5,9]]
输出:[2,3]
解释:
- queries[0] 有两个盘子在蜡烛之间。
- queries[1] 有三个盘子在蜡烛之间。
示例2:
输入:s = `"***|**|*****|**||**|*"` ,queries = [[1,17],[4,5],[14,17],[5,11],[15,16]]
输出:[9,0,0,0,0]
解释:
- queries[0] 有 9 个盘子在蜡烛之间。
- 另一个查询没有盘子在蜡烛之间。
题解一:二分
- 用一数组存储所有蜡烛出现的位置
- 每次询问时,利用二分查找求得区间中第一次蜡烛出现的位置和最后一次蜡烛出现的位置
- 两个位置的距离差区间中蜡烛的数量即为结果
C++代码
class Solution {
public:
vector<int> platesBetweenCandles(string s, vector<vector<int>>& q) {
vector<int> p;vector<int> ans;
for(int i=0;i<s.length();i++){
if(s[i]=='|'){
p.push_back(i);
}
}
for(int i=0;i<q.size();i++){
int l=lower_bound(p.begin(),p.end(),q[i][0])-p.begin();
int r=upper_bound(p.begin(),p.end(),q[i][1])-p.begin();r--;
if(r<=l){
ans.push_back(0);continue;
}
ans.push_back(p[r]-p[l]-(r-l+1)+1);
}
return ans;
}
};
题解二:前缀和
题解一是对于蜡烛位置的处理,我们同样可以思考对盘子进行处理:对盘子数量进行前缀和。
同时可以直接对每个位置左边第一个蜡烛的位置l[i],和右边第一个蜡烛的位置r[i]进行预处理。
- 当询问区间为[x,y]时,通过l[x],r[y]更新区间,
- 此时区间中第一次出现蜡烛的位置为:,最后一次出现蜡烛的位置为:(因为l[i]表示位置i左边第一次出现蜡烛的位置,r[i]同理)
- 于是通过前缀和可以计算区间中满足条件的盘子数量为:,但是由于fx,fy均为蜡烛的位置,可以转化为:(为了防止fx=0的情况,fx-1越界)
C++代码:
class Solution {
public:
vector<int> platesBetweenCandles(string s, vector<vector<int>>& q) {
int n=s.length();
//盘子的前缀和
vector<int> sum(n,0);
//l[i]:位置i左边(包括自身位置)第一个蜡烛的位置
//r[i]:位置i右边(包括自身位置)第一个蜡烛的位置
vector<int> l(n,0),r(n,0);
int left=-1,right=-1;
//先预处理前缀和、l[]
sum[0]=0;
left=s[0]=='|'?0:-1;l[0]=left;
for(int i=1;i<n;i++){
if(s[i]=='*') sum[i]=sum[i-1]+1;
else sum[i]=sum[i-1];
if(s[i]=='|') left=i;
l[i]=left;
}
//再预处理r[]
for(int i=n-1;i>=0;i--){
if(s[i]=='|') right=i;
r[i]=right;
}
vector<int> ans;
int m=q.size();
for(int i=0;i<m;i++){
int x=q[i][0],y=q[i][1];
//记录x右边第一个蜡烛的位置和y左边第一个蜡烛的位置
x=r[x];y=l[y];
//不存在的情况
if(x==-1||y==-1||x>y) ans.push_back(0);
//由于x、y均为蜡烛的位置,期间盘子的数量可以表示为sum[y]-sum[x]
else ans.push_back(sum[y]-sum[x]);
}
return ans;
}
};