C. Perform the Combo
题目大意
给出一个仅由小写字母组成的字符串,再给出一个序列p[]代表在哪些地方按键会出错,每次出错后需要从头开始重新遍历,问所有字母最后分别被按过多少次
解题思路
- 思路1
前缀和+暴力做法,感觉不太好,运行时间太长了 - 先用一个数组sum[]读取整个p[]数组,存下每个位置结尾的次数
- 如果一个位置出了错误 那么这个位置前面所有的字母都需要重复一次
- 最后直接把出现错误的加上就行了
主要讲一下fill()函数和memset()函数 - 因为memset()函数按照字节填充,所以一般memset()只能用来填充char型数组,(因为只有char型占一个字节),如果填充int型数组,只能填充0 -1和INF(正负都行),
memset(arr, val, sizeof arr)
; - 而fill()函数可以赋值任何值,比如int数组:
fill(arr, arr+n,val)
;vector也可以:fill(v.begin(), v.end(), val)
; - memset()函数在头文件
<cstring>
里,fill()函数在头文件<algorithm>
里
注意:不能在没有元素的空容器上调用fill_n()函数
附上代码
#include<bits/stdc++.h>
#define int long long
#define lowbit(x) (x &(-x))
using namespace std;
const int INF=0x3f3f3f3f;
const double PI=acos(-1.0);
const double eps=1e-10;
const int M=1e9+7;
const int N=2e5+5;
typedef long long ll;
typedef pair<int,int> PII;
int sum[N];
int ans[110];
string s;
signed main() {
std::ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int t;
cin>>t;
while(t--){
int n,m;
cin>>n>>m;
cin>>s;
fill(ans,ans+100,0);
fill(sum,sum+N,0);
for(int i=0;i<m;i++){
int temp;
cin>>temp;
sum[0]++;
sum[temp]--;
}
int num=0;
for(int i=0;i<n;i++){
num+=sum[i];//前缀和
ans[s[i]-'a']+=num;
ans[s[i]-'a']++;
}
for (int i=0;i<26;i++)
cout<<ans[i]<<" ";
cout<<endl;
}
return 0;
}
- 思路2
二分 - 先把操作排序,通过lower_bound()函数找到第一个大于 i
的位置k,m-k得到的就是这个位置因为打错而出现的次数,对应的字符加上这个数就行 - lower_bound函数返回的是一个地址,如果要得到位置k需要把它减去首地址,这样就可以找到在数组第某项到第某项过程中的第一个大于某个数的数
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+10;
char s[N];
int p[N],ans[26];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int t;
cin>>t;
while(t--){
int n,m;
cin>>n>>m;
cin>>s+1;
memset(ans,0,sizeof ans);
for(int i=0;i<m;i++)
cin>>p[i];
sort(p,p+m);
for(int i=1;i<=n;i++){
ans[s[i]-'a']++;
int k=lower_bound(p,p+m,i)-p;
ans[s[i]-'a']+=m-k;
}
for(int i=0;i<26;i++)
cout<<ans[i]<<" ";
cout<<endl;
}
return 0;
}
来源:CSDN
作者:Fiveneves
链接:https://blog.csdn.net/Fiveneves/article/details/104502281