链接
http://acm.hdu.edu.cn/showproblem.php?pid=6194
题解
这题要求出现次数恰好等于
K
K
的字符串
可以先统计出现次数大于等于次的,再减去出现次数大于等于
K+1
K
+
1
次的
如果统计出现次数大于等于
K
K
次的,就在后缀数组上搞一个长度的滑动窗口,取这个窗口内的
height
h
e
i
g
h
t
最小值,说明这一段的
K
K
个的出现次数都至少为
K
K
,
再将窗口中的和上方相邻元素取个
min
m
i
n
,减去这个数字,因为这些是重复统计的
这题挺技巧的
代码
//后缀数组
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define maxn 100010
#define maxk 17
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
int ws[maxn], wv[maxn], wa[maxn], wb[maxn], sa[maxn], rk[maxn], N, K, r[maxn], q[maxn], st[maxn][18], height[maxn];
char s[maxn];
bool cmp(int *r, int a, int b, int l){return r[a]==r[b] and r[a+l]==r[b+l];}
void build_sa(int *r, int n, int m)
{
n++;
int i, j, k=0, p, *x=wa, *y=wb, *t;
for(i=0;i<m;i++)ws[i]=0;
for(i=0;i<n;i++)ws[x[i]=r[i]]++;
for(i=1;i<m;i++)ws[i]+=ws[i-1];
for(i=n-1;i>=0;i--)sa[--ws[x[i]]]=i;
for(p=j=1;p<n;j<<=1,m=p)
{
for(p=0,i=n-j;i<n;i++)y[p++]=i;
for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
for(i=0;i<n;i++)wv[i]=x[y[i]];
for(i=0;i<m;i++)ws[i]=0;
for(i=0;i<n;i++)ws[wv[i]]++;
for(i=1;i<m;i++)ws[i]+=ws[i-1];
for(i=n-1;i>=0;i--)sa[--ws[wv[i]]]=y[i];
for(t=x,x=y,y=t,p=1,i=1,x[sa[0]]=0;i<n;i++)
x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
}
for(i=0;i<n;i++)rk[sa[i]]=i;
for(i=0;i<n-1;height[rk[i++]]=k)
for(k?k--:0,j=sa[rk[i]-1];r[i+k]==r[j+k];k++);
}
void init()
{
int i, k;
cl(wa), cl(wb), cl(sa), cl(rk), cl(r);
scanf("%d%s",&K,s);
N=strlen(s);
for(i=0;i<N;i++)r[i]=s[i];
build_sa(r,N,300);
for(i=0;i<=N;i++)st[i][0]=height[i];
for(k=1;k<=maxk;k++)for(i=1;i+(1<<k)-1<=N;i++)st[i][k]=min(st[i][k-1],st[i+(1<<k-1)][k-1]);
}
int qmin(int l, int r)
{
int k=log2(r-l+1);
return min(st[l][k],st[r-(1<<k)+1][k]);
}
int calc(int K)
{
int i, ans=0, l, r;
l=r=1;
if(K>=2)for(i=K-1;i<=N;i++)ans+=qmin(i-K+2,i)-qmin(i-K+1,i);
if(K==1)for(i=1;i<=N;i++)ans+=N-1-sa[i]+1-height[i];
return ans;
}
int main()
{
int T;
for(scanf("%d",&T);T--;)
{
init();
printf("%d\n",calc(K)-calc(K+1));
}
return 0;
}