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\n",ans);
}