HDU 6194 string string string

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

题意: 给一个字符串,和一个k,问你串中出现k次的子串有多少个。

哎,我这次要背大大的锅,从多校开始好多字符串SA的题都在wa,wa,wa,昨天沈阳网选的也是一直处于懵逼状态。还是做题不够多,做题时想的不够啊。 dalao们都说sam(后缀自动机)也可以过,我还是回去补补自动机吧,之后再补一篇博客。

思路:
构建后缀数组后,我们已经把所有后缀全部处理出来了,之后我们对于k>1的情况,我们知道height数组代表着2个排名i,i-1后缀的公共前缀也就是lcp,那么我们按照K长度的区间查找,我们每次查找区间最小值,这个值就是这段区间内可能的结果,之后我们要看一下这段区间i~i+k-1,左边的height以及右边的height的最大值,因为如果存在值那么就说明多加了一部分所以要剪掉,之后如果出现了负值,那么就是为0的情况,之后我们慢慢遍历一遍即可。
对于k=1的情况,我们需要对于字符串总长度len,len-sa[i]代表 sa[i]为排名为i的后缀的起始位置,那么这一段就是可能出现的次数,之后我们减去两边(左边,右边的最大值即可),之后还是与0比较。

    #include 
    #define maxs 2020202
    #define mme(i,j) memset(i,j,sizeof(i))
    #define ll long long
    using namespace std;
    char s[maxs];
    int c[maxs],wa[maxs];
    int wb[maxs],sa[maxs],height[maxs],ranks[maxs];

    void Getsa(int m,int n)
    {
        int *x=wa,*y=wb,p=0;
        for(int i=0; i0;
        for(int i=0; ifor(int i=1; i1];
        for(int i=n-1; i>=0; i--) sa[--c[x[i]]]=i;
        for(int k=1; p1,m=p)
        {
            p=0;
            for(int i=n-k; ifor(int i=0; iif(sa[i]>=k) y[p++]=sa[i]-k;
            for(int i=0; i0;
            for(int i=0; ifor(int i=1; i1];
            for(int i=n-1; i>=0; i--) sa[--c[x[y[i]]]]=y[i];
            swap(x,y);
            x[sa[0]]=0,p=1;
            for(int i=1; iif(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])
                    x[sa[i]]=p-1;
                else x[sa[i]]=p++;
        }
    }

    void gethei()
    {
        int k=0,len=strlen(s);
        for(int i=1; i<=len; i++) ranks[sa[i]]=i;
        for(int i=0; iif(k) k--;
            int j=sa[ranks[i]-1];
            while(s[j+k]==s[i+k])k++;
            height[ranks[i]]=k;
        }
    }

    void text(int n)
    {
        printf("This is SA\n");
        for(int i=0; iprintf("SA[%d] is %d\n",i,sa[i]);
        printf("\nThis is Height\n");
        for(int i=0; i<=n; i++)
            printf("h[%d] is %d\n",i,height[i]);
    }
    int sum[maxs<<2];

    void build(int rt,int l,int r)
    {
        if(l == r)
        {
            sum[rt]=height[l];
            return;
        }
        int mid =(l+r)>>1;
        build(rt<<1|1,mid+1,r);
        build(rt<<1,l,mid);
        sum[rt]=min(sum[rt<<1],sum[rt<<1|1]);
    }

    int ask(int al,int ar,int l,int r,int rt)
    {
        if(al<=l&&ar>=r) return sum[rt];
        int mid=(l+r)>>1;
        int ans=1e9;
        if(mid>=al) ans=ask(al,ar,l,mid,rt<<1);
        if(mid1,r,rt<<1|1));
        return ans;
    }


    int main()
    {
        int t,k;
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d",&k);
            scanf("%s",s);
            int len=strlen(s);
            Getsa(200,len+1);
            gethei();
            build(1,1,len);
            height[len+1]=0;
           // text(len);
            if(k>len)
            {
                puts("0");
                continue;
            }


            int ans=0;
            if(k==1){
                for(int i=1;i<=len-k+1;i++){
                    ans+=max(len-sa[i]-max(height[i],height[i+k]),0);
                }
            printf("%d\n",ans);
            continue;
            }

            for(int i=1;i<=len-k+1;i++){
                ans+=max(ask(i+1,i+k-1,1,len,1)-max(height[i],height[i+k]),0);
            }
            printf("%d\n",ans);
        }
        return 0;
    }

你可能感兴趣的:(字符串,后缀数组)