此题我原本的思路是:建立前缀和数组t[],表示前i个字符的种类总数。对于任意的i,在i+l-1之后找到第一个j使得j>=i+2即可。注意j之后剩下的字符数是否大于等于r。
但这种做法是错误的。考虑IE$abQ
这样的字符串,这种方法会少统计$abQ
从而得到错误答案。 看起来比起从第一个字符开始有多少种类型,我们似乎更应关心从i开始有多少类型。
因此将t数组的定义改为:从i开始,第一次遇到第j,(j∈[0,4))种类型的位置是哪儿。最后找到t[]中第三大的数对应的位置即可。这一过程可以用std::nth_element
完成。
为了 完成上述工作,我们需要一个二维数组记录各种字符出现的位置。
实现中有一些细节,在代码中有注释。
#include <iostream>
#include <algorithm>
#include <cctype>
#include <vector>
const int M=100001;
typedef long long llong;
using namespace std;
char s[M];
vector<int> ch[4];
void getTypes(int n) {
for(int i=0; i<n; i++) {
if(isdigit(s[i])) {
ch[0].push_back(i);
}
else if(islower(s[i])) {
ch[1].push_back(i);
}
else if(isupper(s[i])) {
ch[2].push_back(i);
}
else {
ch[3].push_back(i);
}
}
for(int i=0; i<4; i++) ch[i].push_back(n);
}
int main() {
ios::sync_with_stdio(false);
//0,1,2,3分别代表数字、小写、大写、其他
static int nex[4];
int n,l,r;
llong res=0;
cin>>n>>l>>r;
cin.getline(s,1);
cin.getline(s,n);
//小优化
if(r>=3) {
ch[0].reserve(n/3),ch[1].reserve(n/3),ch[2].reserve(n/3),ch[3].reserve(n/3);
getTypes(n);
for(int i=0; i<n; i++) {
for(int j=0; j<4; j++) {
//有可能在i+l-1之前就有三个了
nex[j]=lower_bound(ch[j].begin(),ch[j].end(),i)-ch[j].begin();
nex[j]=ch[j][nex[j]];
}
nth_element(nex,nex+2,nex+4);
int third=nex[2],term=min(n,r+i);
//由于有l,有贡献区间起始有两种情况:1.third 2.i
int nowRes=min(term-third,term-(l+i-1));
/* 注意
1:对于上面两种情况,区间终止都要考虑两种情况(对应L45 term)
2:由于有r,区间可能并不产生贡献
*/
res+=(llong)max(nowRes,0);
}
}
cout<<res;
return 0;
}