智乃的密码

题目链接
 

智乃去注册账号,他发现网站的的密码必须符合以下几个条件

  • 密码是仅包含大小写英文字母、数字、特殊符号的字符串。
  • 密码的长度不少于L{L}L个字符,并且不多于R{R}R个字符。
  • 密码中应该至少包括①大写英文字母、②小写英文字母、③数字、④特殊符号这四类字符中的三种。

所谓特殊字符,是指非大小写字母、数字以及空格回车等不可见字符的可见字符,包括但不限于"~!@#$%^&*()_+"。

现在智乃有一个长度大小为N{N}N的字符串S{S}S,她想知道S{S}S串中有多少个子串是一个符合条件的密码,请你帮助智乃统计符合条件的密码数目。

子串是指字符串中某一段连续的区间,例如对于字符串"abcde"来说,"abc","cde"都是它的子串,而"ace"不是它的子串。

输入描述:

 

第一行输入三个正整数N,L,R(1≤N≤105,1≤L≤R≤N){N,L,R(1\le N \le 10^5,1\le L\le R \le N)}N,L,R(1≤N≤105,1≤L≤R≤N),表示S{S}S串的长度,合法密码长度应该在L{L}L到R{R}R个字符之间。

接下来一行输入一个长度为N{N}N的字符串S{S}S,字符串仅包括①大写英文字母、②小写英文字母、③数字、④特殊符号四类字符。

输出描述:

仅一行一个整数,表示有多少子串是一个合法的密码。

示例1

输入

10 6 8
asdfeg111*

输出

3

说明

"eg111*","feg111*","dfeg111*"

思路:假设x是最小满足的个数,如果y>=x,那么y是不是也是满足答案的,这不就是转化为了最大中找最小,那么就要开始二分了。我们直接二分区间,因为是区间,我们记录的满足条件也要是区间所以这里我们就要用前缀和记录每个种类的个数了。

二分区间:          1.我们求的是个数,不是值,所以要二分区间。

                            2.二分区间只需要将当前位置i与mid传进去即可。

数组从1开始的:1.如果想要从L开始,就要减去i带来的偏移量。(注意从哪个位置开始,是否是    (求位置)         当前的L位置开始,如果是L当前位置开始就需要减去偏移量)

                           2.区间的循环要减去一段,如果下文的位置需要减去偏移量,那么这里就需要                                 再减去的部分加上偏移量。

#include <bits/stdc++.h>

using namespace std;

#define int long long
const int mod=1e9+7;

const int N=1e5+10;
int a[N],st[N],b[N],c[N],d[N];
int n,L,R;
char s[N];

bool chk(int l,int r)
{
    int x=a[r]-a[l-1],y=b[r]-b[l-1],z=c[r]-c[l-1],p=d[r]-d[l-1];
    if((x>=1&&y>=1&&z>=1)||(x>=1&&y>=1&&p>=1)||(x>=1&&z>=1&&p>=1)||(y>=1&&z>=1&&p>=1))return 1;
    return 0;
}

signed main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cin>>n>>L>>R;
    cin>>s+1;
    for(int i=1;i<=n;i++)
    {
        a[i]=a[i-1];
        b[i]=b[i-1];
        c[i]=c[i-1];
        d[i]=d[i-1];
        if(s[i]>='A'&&s[i]<='Z') a[i]++;
        else if(s[i]>='a'&&s[i]<='z') b[i]++;
        else if(s[i]>='0'&&s[i]<='9') c[i]++;
        else d[i]++;
    }

    int ans=0;
    for(int i=1;i<=n-L+1;i++)//i会减一,将最后允许的位置补上
    {
        int l=min(n,i+L-1),r=min(n,i+R-1);//减1是为了消除i带来的偏移量,可以让从l位置开始
        int y=r;
        while(l<r)
        {
            int mid=l+r>>1;
            if(chk(i,mid))r=mid;
            else l=mid+1;
        }
        if(!chk(i,r))continue;
        ans+=y-r+1;
    }
    cout<<ans<<endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值