题目
题目链接:https://codeforces.com/problemset/problem/700/E
给定一个字符串 (S),要求构造字符串序列 (s_1,s_2,ldots,s_k),满足任意 (s_i) 都是 (S) 的子串,且任意 (iin[2,n]),都有 (s_{i-1}) 在 (s_i) 中出现了至少 (2) 次(可以有重叠部分,只要起始、结尾位置不同即可)。
求可能的最大的 (k) 的值(即序列的最大可能长度)。
(nleq 2 imes 10^5)。
思路
首先有一个比较显然的结论,最终序列任意 (s_i,s{i+1}) 都应该满足 (s_i) 是 (s_{i+1}) 的后缀。因为如果不是,那么删掉 (s_{i+1}) 后面若干个字符直到 (s_i) 是其后缀显然不劣。
那么对于 parent 树上的两个节点 (x,y),假设 (x) 是 (y) 的祖先,那么如果 (x) 所表示的等价类中长度最长的串在 (y) 长度最长的串中出现了至少两次,那么就可以从 (x) 转移到 (y)。
由于 (x) 中任意一个字符串必然是 (y) 的后缀,所以对于 (y) 的 (mathrm{endpos}) 集合中任意一个元素 (mathrm{pos}_y),如果存在一个 (mathrm{pos}_x) 满足 (mathrm{pos}_xin[mathrm{pos}_y-mathrm{len}_y+mathrm{len}_x,mathrm{pos}_y)),那么 (x) 就至少在 (y) 的任意串中出现了至少两次。
所以我们先把 SAM 建好,用可持久化线段树合并求出每一个节点的 (mathrm{endpos}),然后在 parent 树上 dp 即可。
注意任意节点不一定只能从其父亲转移而来,所以需要记录一个 (g_x) 表示节点 (x) 是被哪一个节点转移到的。
时间复杂度 (O(nlog n))。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=400010,LG=20;
int n,tot,ans,head[N],rt[N],a[N],b[N],f[N],g[N];
char s[N];
struct edge
{
int next,to;
}e[N];
void add(int from,int to)
{
e[++tot]=(edge){head[from],to};
head[from]=tot;
}
struct SegTree
{
int tot,lc[N*LG*4],rc[N*LG*4];
bool flag[N*LG*4];
int ins(int l,int r,int k)
{
int x=++tot;
flag[x]=1;
if (l==r) return x;
int mid=(l+r)>>1;
if (k<=mid) lc[x]=ins(l,mid,k);
if (k>mid) rc[x]=ins(mid+1,r,k);
return x;
}
int merge(int x,int y)
{
if (!x || !y) return x|y;
int p=++tot;
flag[p]=flag[y]|flag[x];
lc[p]=merge(lc[x],lc[y]);
rc[p]=merge(rc[x],rc[y]);
return p;
}
int query(int x,int l,int r,int ql,int qr)
{
if (ql<=l && qr>=r) return flag[x];
int mid=(l+r)>>1,s=0;
if (ql<=mid) s|=query(lc[x],l,mid,ql,qr);
if (qr>mid) s|=query(rc[x],mid+1,r,ql,qr);
return s;
}
}seg;
struct SAM
{
int tot,last,len[N],pos[N],fa[N],ch[N][26];
SAM() { tot=last=1; }
void ins(int c,int id)
{
int p=last,np=++tot;
last=np; len[np]=len[p]+1; pos[np]=id;
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=++tot;
len[nq]=len[p]+1; fa[nq]=fa[q]; pos[nq]=pos[q];
for (int i=0;i<26;i++) ch[nq][i]=ch[q][i];
fa[np]=fa[q]=nq;
for (;p && ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
}
}
}
void buildseg()
{
for (int i=2;i<=tot;i++)
rt[i]=seg.ins(1,n,pos[i]);
for (int i=1;i<=tot;i++) b[len[i]]++;
for (int i=1;i<=tot;i++) b[i]+=b[i-1];
for (int i=1;i<=tot;i++) a[b[len[i]]--]=i;
for (int i=tot;i>=1;i--)
{
int j=a[i];
if (fa[j]!=1)
rt[fa[j]]=seg.merge(rt[fa[j]],rt[j]);
add(fa[j],j);
}
}
void dfs(int x)
{
for (int i=head[x];~i;i=e[i].next)
{
int v=e[i].to;
if (x!=1 && seg.query(rt[g[x]],1,n,pos[v]-len[v]+len[g[x]],pos[v]-1))
f[v]=f[g[x]]+1,g[v]=v;
else if (x!=1)
f[v]=f[g[x]],g[v]=g[x];
else
f[v]=1,g[v]=v;
ans=max(ans,f[v]);
dfs(v);
}
}
}sam;
int main()
{
memset(head,-1,sizeof(head));
scanf("%d%s",&n,s+1);
for (int i=1;i<=n;i++)
sam.ins(s[i]-'a',i);
sam.buildseg();
sam.dfs(1);
printf("%d",ans);
return 0;
}