并不对劲的spoj nsubstr

题意是求一个字符串每个长度的子串出现次数最多的那个出现了多少次,也就是求每个到根的最长路的right集合最大值 。

先建后缀自动机,然后将每个前缀所在的集合的初值设为1,因为所有前缀的right集合肯定不相同,而且它们包含了所有位置。

接下来按到根的最长距离从大到小排序,将right集合累加到parent上。这么排序是因为到根的最长距离长的状态肯定不是到根的最长距离短的状态的parent。

最后直接求到根的不同的最长距离的最大的right集合就行。

 

#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#define maxn 250010
using namespace std;
int ans,len,p;
int read()
{
    int f=1,x=0;char ch=getchar();
    while(isdigit(ch)==0 && ch!='-')ch=getchar();
    if(ch=='-')f=-1,ch=getchar();
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return x*f;
}
void write(int x)
{
    int ff=0;char ch[15];
    while(x)ch[++ff]=(x%10)+'0',x/=10;
    if(ff==0)putchar('0'); 
    while(ff)putchar(ch[ff--]);
    putchar('\n');
}
typedef struct node
{
    int to[30],dis,fa;
}spot;
//求每个dis的right集合最大值 
//不是每个状态的right集合!! 
//ass♂we♂can♂
struct SAM
{
    spot x[maxn<<1];
    int ls,ls2,q,cnt,rt,lst,c[maxn<<1],ord[maxn<<1],ans[maxn<<1],maxl[maxn<<1];
    int right[maxn<<1],f[maxn<<1];
    char s[maxn];
    inline void start()
    {
        lst=rt=++cnt;
        scanf("%s",s+1);
        ls=strlen(s+1);
        for(int i=1;i<=ls;i++)
            extend(i);
    }
    inline void extend(int pos)
    {
        int val=s[pos]-'a',p=lst,np=++cnt;
        lst=np,x[np].dis=pos;
        for(;p&&x[p].to[val]==0;p=x[p].fa)x[p].to[val]=np;
        if(p==0)x[np].fa=rt;
        else
        {
            int q=x[p].to[val];
            if(x[q].dis==x[p].dis+1)x[np].fa=q;
            else
            {
                int nq=++cnt;
                x[nq].dis=x[p].dis+1;
                memcpy(x[nq].to,x[q].to,sizeof(x[q].to));
                x[nq].fa=x[q].fa,x[np].fa=x[q].fa=nq;
                for(;x[p].to[val]==q;p=x[p].fa)x[p].to[val]=nq;
            }
        }
    }
    inline void qsort()
    {
        for(int i=1;i<=cnt;i++)
            c[x[i].dis]++; 
        for(int i=1;i<=ls;i++)
            c[i]+=c[i-1];
        for(int i=1;i<=cnt;i++)
            ord[c[x[i].dis]--]=i; 
    }
    inline void work()
    {
        for(int i=1,p=rt;i<=ls;i++)
            p=x[p].to[s[i]-'a'],right[p]=1;
        for(int i=cnt;i>=1;i--){int u=ord[i];right[x[u].fa]+=right[u];}
        for(int i=1;i<=cnt;i++)f[x[i].dis]=max(f[x[i].dis],right[i]);
        for(int i=ls;i>=1;i--)f[i]=max(f[i],f[i+1]);
        for(int i=1;i<=ls;i++)write(f[i]);
    //    cout<<"He doesn't have hands->"<<endl;
    }
}t;
int main()
{
    t.start();
    t.qsort();
    t.work();
    return 0;
}
并不对劲的SAM

 

听某位大佬说后缀自动机能解决所有不是字符串dp的字符串题。

posted @ 2018-01-19 17:48  echo6342  阅读(157)  评论(0编辑  收藏  举报