题目链接:https://ac.nowcoder.com/acm/contest/23478/I
题面:
思路:可以参考这里
以下摘自官方题解
同时这6个条件在固定所选子串的其中一个端点后,另一个端点的合法性是单调的。
所以可以使用二分或者尺取的方式直接判断每一个条件的合法区间。
一点理解和优化:
1)首先为什么border()的返回值为tmp[1];仔细阅读题干,题目要求密码中至少包含四类字符的3种,而sort默认为升序,所以只要tmp[1] > 0 ,那么一定会存在3种不同符号满足要求;而如果border返回值为0,那么rr一定 <= 0,而 ll >= 1,所以calcu返回值为0;
2)在官方题解种通过前缀和数组和一层循环来更新某一类字符出现的位置,其实我们直接将该类字符的下标,用一个标记数组mar来记录就行了
3)另外,ans应该开long long ,不然会被卡。
具体参考代码实现
AC代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5 + 5;
char s[N];
int l, r, n;
long long ans;
int mar[4];
int type(char ch){
if(ch >= '0' && ch <= '9') return 0;
if(ch >= 'a' && ch <= 'z') return 1;
if(ch >= 'A' && ch <= 'Z') return 2;
else return 3;
}
int border(){
int tmp[4];
memcpy(tmp, mar, sizeof(mar));
sort(tmp, tmp + 4);
return tmp[1];
}
int calcu(int x){
int ll = 1, rr = n;
ll = max(ll, x - r + 1);
rr = min(rr, x - l + 1);
rr = min(rr, border());
return ll <= rr ? rr - ll + 1 : 0;
}
int main(){
scanf("%d%d%d", &n, &l, &r);
scanf("%s", s + 1);
for(int i = 1; i <= n; ++i){
mar[type(s[i])] = i;
ans += calcu(i);
}
printf("%lld\n", ans);
system("pause");
return 0;
}```