hdu 6194string string string 后缀自动机

版权声明:本文为博主原创文章,转载请附上原博客链接。 https://blog.csdn.net/Dale_zero/article/details/82951788

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6194

查询原串中出现次数等于k次的子串数量。需要用到基数排序。

构造完后缀自动机之后将节点按照maxlen值从小到大进行排序。

因为某个节点p的父节点fp的maxlen值一定比p的maxlen小(因为其right集合比p大,right(p)是right(fp)的子集),所以fp一定排在p前面。

最初始将每个节点的cnt值赋为1,表示该节点代表子串在原串中的出现次数(或者说是right集合的元素数量,一个意思)。排完序之后倒着遍历,将每个节点的cnt值加到其父节点上(因为一个串出现了k次,其后缀一定也出现了k次)。并且因为后缀自动机的性质,拥有相同父节点的节点们的right集合没有交集,所以不会重复计算。

顺便更新了一波模板

模板:



const int N = 4e5+10;///N为字符串长,后缀自动机节点数不超过2N-2所以要左移一位
char s[N];
const int LetterSize = 26;

int tot, last,ch[N<<1][LetterSize],fa[N<<1],len[N<<1];
int sum[N<<1],tp[N<<1];///tp【i】:max值为第i小的节点序号。
int root=1;

void init()
{
    last = tot = 1;
    len[1] = 0;
    memset(ch,0,sizeof ch);
    memset(fa,0,sizeof fa);
}

void add( int x)
{
    int p = last, np = last = ++tot;
    len[np] = len[p] + 1;
    while( p && !ch[p][x]) ch[p][x] = np, p = fa[p];
    if(p == 0) fa[np] = 1;
    else
    {
        int q = ch[p][x];
        if( len[q] == len[p] + 1)
            fa[np] = q;
        else
        {
            int nq = ++tot;
            memcpy( ch[nq], ch[q], sizeof ch[q]);
            len[nq] = len[p] + 1, fa[nq] = fa[q], fa[q] = fa[np] = nq;
            while( p && ch[p][x] == q)  ch[p][x] = nq, p = fa[p];
        }
    }
}



void toposort()
{
    for(int i = 1; i <= len[last]; i++)   sum[i] = 0;
    for(int i = 1; i <= tot; i++)   sum[len[i]]++;
    for(int i = 1; i <= len[last]; i++)   sum[i] += sum[i-1];
    for(int i = 1; i <= tot; i++)   tp[sum[len[i]]--] = i;
}

题目代码:

#include<bits/stdc++.h>
#define LL long long
using namespace std;

const int N = 1e5+10;
char s[N];
const int LetterSize = 26;

int tot, last,ch[N<<1][LetterSize],fa[N<<1],len[N<<1];
LL cnt[N<<1];
int sum[N<<1],tp[N<<1];///tp【i】:max值为第i小的节点序号。

void init()
{
    last = tot = 1;
    len[1] = 0;
    memset(ch,0,sizeof ch);
    memset(fa,0,sizeof fa);
    memset(cnt,0,sizeof cnt);
}

void add( int x)
{
    int p = last, np = last = ++tot;
    len[np] = len[p] + 1;
    cnt[last]=1;
    while( p && !ch[p][x]) ch[p][x] = np, p = fa[p];
    if(p == 0) fa[np] = 1;
    else
    {
        int q = ch[p][x];
        if( len[q] == len[p] + 1)
            fa[np] = q;
        else
        {
            int nq = ++tot;
            memcpy( ch[nq], ch[q], sizeof ch[q]);
            len[nq] = len[p] + 1, fa[nq] = fa[q], fa[q] = fa[np] = nq;
            while( p && ch[p][x] == q)  ch[p][x] = nq, p = fa[p];
        }
    }
}



void toposort()
{
    for(int i = 1; i <= len[last]; i++)   sum[i] = 0;
    for(int i = 1; i <= tot; i++)   sum[len[i]]++;
    for(int i = 1; i <= len[last]; i++)   sum[i] += sum[i-1];
    for(int i = 1; i <= tot; i++)   tp[sum[len[i]]--] = i;
}


int main()
{
     int t;
     scanf("%d",&t);
     while(t--)
     {
         LL k;
         init();
         scanf("%lld",&k);
         scanf("%s",s);
         for(int i=0;s[i];i++)
         {
             add(s[i]-'a');
         }
         toposort();
         LL ans=0;
         for(int i=tot;i>=1;i--)
         {
             int p=tp[i],fp=fa[p];
             cnt[fp]+=cnt[p];
             if(cnt[p]==k)
                ans+=len[p]-len[fp];
         }
         printf("%lld\n",ans);
     }
     return 0;
}

猜你喜欢

转载自blog.csdn.net/Dale_zero/article/details/82951788