SPOJ 8222 NSUBSTR - Substrings

http://www.spoj.com/problems/NSUBSTR/

 

题意:

F(x)定义为字符串S中所有长度为x的子串重复出现的最大次数

输出F[1]~F[len(S)]

 

用字符串S构建后缀自动机

若子串 str ∈状态s,那么子串str 在字符串S中出现的次数就是| Right(s) |

显然不能枚举所有状态的所有子串

但是我们可以线性的时间得到F[Max(s)]= | Right(s) |

然后再对F做一个后缀最大值即可

 

如何得到 一个状态Right集合的大小?

一个状态s的Right集合就是Parent树上,s的子树中叶子节点Right集合的并集

所以可以 从parent树上 底层向上层更新,即将底层节点的Right并入父节点的Right

这个可以通过拓扑排序实现 

 

 

#include<cstdio>
#include<cstring>

#define N 250002

#define max(x,y) ((x)>(y) ? (x) : (y))

char s[N];

int tot=1,fa[N<<1],len[N<<1],ch[N<<1][26];
int last=1,p,np,q,nq;

int front[N<<1],nxt[N<<1],to[N<<1],cnt;

int r[N<<1],f[N];
int v[N<<1],sa[N<<1];

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

int main()
{
    scanf("%s",s+1);
    int n=strlen(s+1);
    for(int i=1;i<=n;++i) 
    {
        r[tot+1]=1;
        extend(s[i]-'a');
    }
    for(int i=1;i<=tot;++i) v[len[i]]++;
    for(int i=1;i<=n;++i) v[i]+=v[i-1];
    for(int i=1;i<=tot;++i) sa[v[len[i]]--]=i;
    for(int i=tot;i;--i) r[fa[sa[i]]]+=r[sa[i]];
    for(int i=1;i<=tot;++i) f[len[i]]=max(f[len[i]],r[i]);
    for(int i=n-1;i;--i) f[i]=max(f[i],f[i+1]); 
    for(int i=1;i<=n;++i) printf("%d\n",f[i]);
}

 

posted @ 2018-03-15 19:52  TRTTG  阅读(277)  评论(0编辑  收藏  举报