HDU6194 string string string

链接

http://acm.hdu.edu.cn/showproblem.php?pid=6194

题解

这题要求出现次数恰好等于 K 的字符串
可以先统计出现次数大于等于 K 次的,再减去出现次数大于等于 K + 1 次的
如果统计出现次数大于等于 K 次的,就在后缀数组上搞一个长度 K 1 的滑动窗口,取这个窗口内的 h e i g h t 最小值,说明这一段的 K L C P 的出现次数都至少为 K
再将窗口中的 h e i g h t 和上方相邻元素取个 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;
}

猜你喜欢

转载自blog.csdn.net/FSAHFGSADHSAKNDAS/article/details/81412046