CF700E Cool Slogans

Link
先建出SAM并维护每个endpos集合中元素的最后出现位置(pos)
那么对于一个点(p),若(link_p)的endpos集合中有元素在([pos_p-len_p+len_{link_p},pos_p-1])中出现,那么说明(link_p)中的元素必在(p)的元素中出现至少两次。
那么利用线段树合并维护出现位置,然后从上往下dp即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
const int N=400007,M=N*25;
int cnt=1,now=1,tot,ch[N][26],len[N],link[N],a[N],top[N],pos[N],f[N],root[N],ls[M],rs[M];char str[N];
void extend(int c)
{
    int p=now,q;now=++cnt,pos[now]=len[now]=len[p]+1;
    for(;p&&!ch[p][c];p=link[p]) ch[p][c]=now;
    if(!p) return link[now]=1,void();
    if(len[q=ch[p][c]]==len[p]+1) return link[now]=q,void();
    link[++cnt]=link[q],memcpy(ch[cnt],ch[q],sizeof ch[q]),len[cnt]=len[p]+1,link[now]=link[q]=cnt,pos[cnt]=pos[q];
    for(;ch[p][c]==q;p=link[p]) ch[p][c]=cnt;
}
#define mid ((l+r)>>1)
void update(int&p,int l,int r,int x)
{
    if(p=++tot,l==r) return ;
    x<=mid? update(ls[p],l,mid,x):update(rs[p],mid+1,r,x);
}
int merge(int u,int v)
{
    if(!u||!v) return u|v; int t=++tot;
    return ls[t]=merge(ls[u],ls[v]),rs[t]=merge(rs[u],rs[v]),t;
}
int query(int p,int l,int r,int L,int R){return p&&((L<=l&&r<=R)||((L<=mid&&query(ls[p],l,mid,L,R))||(R>mid&&query(rs[p],mid+1,r,L,R))));}
#undef mid
void sort()
{
    static int c[N];
    for(int i=1;i<=cnt;++i) ++c[len[i]];
    for(int i=1;i<=cnt;++i) c[i]+=c[i-1];
    for(int i=1;i<=cnt;++i) a[c[len[i]]--]=i;
}
int main()
{
    int n,ans=1;
    scanf("%d%s",&n,str+1);
    for(int i=1;i<=n;++i) extend(str[i]-'a'),update(root[now],1,n,i);
    sort();
    for(int i=cnt;i^1;--i) root[link[a[i]]]=merge(root[link[a[i]]],root[a[i]]);
    for(int i=2,u,p,x;i<=cnt;++i)
	if((p=link[u=a[i]])==1) f[u]=1,top[u]=u;
        else query(root[top[p]],1,n,pos[u]-len[u]+len[top[p]],pos[u]-1)? (f[u]=f[p]+1,top[u]=u):(f[u]=f[p],top[u]=top[p]),ans=std::max(ans,f[u]);
    printf("%d
",ans);
}
原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12489052.html