SPOJ8222: NSUBSTR - Substrings

【传送门:SPOJ8222


简要题意:

  给出一个字符串S,令F(x)表示S的所有长度为x的子串中,出现次数的最大值。求F(1)..F(Lengh(S));


题解:

  后缀自动机的模板题(会SAM是没有用的,会DP和广义才有用——Cherish_OI)

  只要求出每个状态的Right集合的个数就可以求出当前状态的相同子串的个数

  然后DP就好了


参考代码:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
struct SAM
{
    int son[27],fail,dep;
}tr[510000];int cnt,last,root;
int a[310000];
void add(int k)
{
    int x=a[k];
    int np=++cnt,p=last;tr[np].dep=k;
    while(p!=0&&tr[p].son[x]==0) tr[p].son[x]=np,p=tr[p].fail;
    if(p==0) tr[np].fail=root;
    else
    {
        int q=tr[p].son[x];
        if(tr[q].dep==tr[p].dep+1) tr[np].fail=q;
        else
        {
            int nq=++cnt;tr[nq]=tr[q];
            tr[nq].dep=tr[p].dep+1;
            tr[q].fail=tr[np].fail=nq;
            while(p!=0&&tr[p].son[x]==q) tr[p].son[x]=nq,p=tr[p].fail;
        }
    }
    last=np;
}
int r[510000],f[310000];
char st[310000];
int Rsort[5100000],sa[510000];
int main()
{
    scanf("%s",st+1);
    int len=strlen(st+1);
    cnt=root=last=1;
    for(int i=1;i<=len;i++) a[i]=st[i]-'a'+1,add(i);
    for(int i=1;i<=cnt;i++) Rsort[tr[i].dep]++;
    for(int i=1;i<=len;i++) Rsort[i]+=Rsort[i-1];
    for(int i=1;i<=cnt;i++) sa[Rsort[tr[i].dep]--]=i;
    int p=root;
    for(int i=1;i<=len;i++) p=tr[p].son[a[i]],r[p]++;
    for(int i=cnt;i>=1;i--) r[tr[sa[i]].fail]+=r[sa[i]];
    memset(f,0,sizeof(f));
    for(int i=1;i<=cnt;i++) f[tr[i].dep]=max(f[tr[i].dep],r[i]);
    for(int i=len-1;i>=1;i--) f[i]=max(f[i+1],f[i]);
    for(int i=1;i<=len;i++) printf("%d\n",f[i]);
    return 0;
}

 

posted @ 2018-04-12 08:14  Star_Feel  阅读(123)  评论(0编辑  收藏  举报