Boring count
给定一个字符串S,求S的子串个数满足子串里每个字符的个数不超过k。
枚举字符串下标i,每次计算以i为结尾的符合条件的最长串。那么以i为结尾的符合条件子串个数就是最长串的长度。求和即可。
计算以i为结尾的符合条件的最长串两种方法:
1.维护一个起点下标startPos,初始为1。如果当前为i,那么cnt[str[i]]++,如果大于k的话,就while( str[startPos] != str[i+1] ) cnt[str[startPos]]--, startPos++; 每次都保证 startPos~i区间每个字母个数都不超过k个。ans += ( i-startPos+1 )。 时间复杂度O(n)
2.预处理出所有字母的前缀和。然后通过二分找出以i为结尾的符合条件的最长串的左边界。时间复杂度O(nlogn),写的不够好的可能超时。
方法一:
#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define M 100000
using namespace std;
typedef __int64 LL;
char s[M+5];
int f[30];
int main()
{
int T,k,n;
int l,r;
LL ans;
scanf("%d",&T);
while(T--)
{
scanf("%s%d",s,&k);
n=strlen(s);
ans=0;
r=l=0;
memset(f,0,sizeof(f));
while(l<n)
{
while(r<n)
{
int x=s[r]-'a';
if(f[x]>=k) {
break;
}
f[x]++;
r++;
}
f[s[l]-'a']--;
// printf("*%d :%d\n",s[l],f[s[l]]);
ans+=r-l;
// printf("l=%d ,r=%d :ans=%d\n",l,r,ans);
l++;
}
printf("%I64d\n",ans);
}
return 0;
}
方法二:
#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define M 100000
using namespace std;
typedef __int64 LL;
char s[M+5];
int f[M+5][30];
int k;
int ok(int mid,int now)
{
for(int i=0;i<26;i++)
if(f[now][i]-f[mid-1][i]>k) return 0;
return 1;
}
int main()
{
int T,n;
int l,r;
LL ans;
scanf("%d",&T);
while(T--)
{
scanf("%s%d",s,&k);
n=strlen(s);
for(int i=0;i<26;i++)
f[0][i]=0;
for(int i=1;i<=n;i++)
{
for(int j=0;j<26;j++)
f[i][j]=f[i-1][j];
f[i][s[i-1]-'a']++;
}
ans=0;
for(int i=1;i<=n;i++)
{
int l,r,mid;
l=1; r=n;
int x;
while(l<=r)
{
mid=(l+r)>>1;
if(ok(mid,i)) {r=mid-1;x=mid;}
else l=mid+1;
}
// printf("%d:%d\n",i,x);
ans+=i-x+1;
}
printf("%I64d\n",ans);
}
return 0;
}