一、题目
二、解法
可以建好后缀自动机后 d p dp dp,容易发现我们只需要考虑一条链上的选取情况(即一定包含后缀关系,如果不包含则在另一条链上被考虑过了),设 f [ u ] f[u] f[u]为自上到下 d p dp dp到 u u u,所取得的最大长度,我们还要维护一个决策点, d p dp dp时需要判断父亲的决策点是否在该节点代表的串中出现了两次。
由于一定有后缀关系,我们只需要判断决策点的 e n d p o s endpos endpos集合中 [ p o s [ u ] − l e n [ u ] + l e n [ t ] , p o s [ u ] − 1 ] [pos[u]-len[u]+len[t],pos[u]-1] [pos[u]−len[u]+len[t],pos[u]−1]中有串出现,其中 t t t是决策点, p o s pos pos是当前点任意一个 e n d p o s endpos endpos,出现两次意味着在任意一个地方都出现了两次,看在此范围内有没有串即可,这部分需要用到线段树合并维护 e n d p o s endpos endpos集合。
如果出现了两次, f [ u ] = f [ t ] + 1 f[u]=f[t]+1 f[u]=f[t]+1, u u u的决策点为它自身,否则 f [ u ] = f [ f a ] f[u]=f[fa] f[u]=f[fa],它的决策点就是父亲的决策点,这里其实有一点贪心的意味,即我们的决策点在越上面越好。
时间复杂度 O ( n ) O(n) O(n),口胡可能难以理解的地方请参考我的代码。
可以说明一个节点的所有串是等价的,所以只需要考虑最长串。
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int M = 400005;
int read()
{
int x=0,flag=1;
char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,m,k,tot,f[M],cnt,last,len[M],fa[M],ch[M][26];
int Index,mi[M],rt[M],ls[20*M],rs[20*M],sum[20*M];char s[M];
int dp[M],top[M],ans;
struct edge
{
int v,next;
edge(int V=0,int N=0) : v(V) , next(N) {}
}e[2*M];
void ins(int &x,int l,int r,int id)
{
x=++Index;
sum[x]=1;
if(l==r) return ;
int mid=(l+r)>>1;
if(mid>=id) ins(ls[x],l,mid,id);
else ins(rs[x],mid+1,r,id);
}
void add(int c)
{
int p=last,np=last=++cnt;
ins(rt[np],1,n,mi[np]=len[np]=len[p]+1);
for(; p && !ch[p][c]; p=fa[p]) ch[p][c]=np;
if(!p) fa[np]=1;
else
{
int q=ch[p][c];
if(len[q]==len[p]+1) fa[np]=q;
else
{
int nq=++cnt;
fa[nq]=fa[q];
memcpy(ch[nq],ch[q],sizeof ch[q]);
len[nq]=len[p]+1;
fa[q]=fa[np]=nq;
for(; p && ch[p][c]==q; p=fa[p]) ch[p][c]=nq;
}
}
}
int merge(int x,int y)
{
if(!x || !y) return x|y;
int p=++Index;
sum[p]=sum[x]+sum[y];
ls[p]=merge(ls[x],ls[y]);
rs[p]=merge(rs[x],rs[y]);
return p;
}
int ask(int i,int l,int r,int L,int R)
{
if(!i || L>r || l>R || L>R) return 0;
if(L<=l && r<=R) return sum[i];
int mid=(l+r)>>1;
return ask(ls[i],l,mid,L,R)+ask(rs[i],mid+1,r,L,R);
}
void dfs(int u)
{
for(int i=f[u]; i; i=e[i].next)
{
int v=e[i].v;
if(v==fa[u]) continue;
dfs(v);
rt[u]=merge(rt[u],rt[v]);
mi[u]=min(mi[u],mi[v]);
}
}
void dfs2(int u)
{
int t=top[fa[u]];
if(fa[u]==1)
{
dp[u]=1;
top[u]=u;
}
else
{
if(ask(rt[t],1,n,mi[u]-len[u]+len[t],mi[u]-1))
dp[u]=dp[t]+1,top[u]=u;
else
dp[u]=dp[fa[u]],top[u]=top[fa[u]];
}
ans=max(ans,dp[u]);
for(int i=f[u];i;i=e[i].next)
{
int v=e[i].v;
if(v==fa[u]) continue;
dfs2(v);
}
}
int main()
{
cnt=last=1;
n=read();scanf("%s",s);
for(int i=0;i<n;i++) add(s[i]-'a');
for(int i=2;i<=cnt;i++)
{
if(!mi[i]) mi[i]=n+1;
e[++tot]=edge(i,f[fa[i]]),f[fa[i]]=tot;
e[++tot]=edge(fa[i],f[i]),f[i]=tot;
}
dfs(1);
ans=1;
dfs2(1);
printf("%d\n",ans);
}