题目分析
实际上,分出来的所有 t t t的长度应该是连续的,因为如果不连续的话,删掉过长的 t t t首尾几个字符即可。于是可知, t i + 1 t_{i+1} ti+1应该是 t i t_i ti删掉首字母或者尾字母形成的字符串。
将原串反过来,前缀就变成了后缀。
设 d p ( i ) dp(i) dp(i)表示以字符 i i i结尾划分为最后一个字符串,可以划分的最大长度。
由于以 i i i结尾的字符串和以 i − 1 i-1 i−1结尾的字符串,会有一些公共部分,所以 d p ( i − 1 ) ≥ d p ( i ) − 1 dp(i-1) \geq dp(i)-1 dp(i−1)≥dp(i)−1。于是就可以线性地枚举 d p ( i ) dp(i) dp(i)的值,假设是 x x x。
这个 x x x合法的条件是,存在小于等于位置 i − x i-x i−x的位置,和前缀 i i i或前缀 i − 1 i-1 i−1的最长公共后缀长度大于等于 x − 1 x-1 x−1的, d p dp dp值大于等于 x − 1 x-1 x−1。
满足第一个条件的位置在我们线性枚举 d p ( i ) dp(i) dp(i)时是也是线性增加的,用一个指针去扫即可。
在SAM的parent树上,从代表前缀 i i i或代表前缀 i − 1 i-1 i−1的节点开始跳父亲,跳到最浅的一个满足 s t e p ( x ) ≥ x − 1 step(x) \geq x-1 step(x)≥x−1的点,然后这个点的子树里的点代表的字符串都符合第二个条件,用dfs序将子树信息转成序列的一段区间。
第三个条件就以dfs序为下标,线段树维护区间最大值即可。
代码
#include<bits/stdc++.h>
using namespace std;
#define RI register int
const int N=1000005;
int n,SZ,last,ans,tot,tim;char S[N];
int ch[N][26],pre[N],step[N],pos[N];
int h[N],ne[N<<1],to[N<<1],f[N][20],in[N],out[N],dp[N],mx[N<<2];
void ins(int x,int id) {
int np=++SZ,p=last;
pos[id]=last=np,step[np]=step[p]+1;
while(p&&!ch[p][x]) ch[p][x]=np,p=pre[p];
if(!p) pre[np]=1;
else {
int q=ch[p][x];
if(step[q]==step[p]+1) pre[np]=q;
else {
int nq=++SZ;step[nq]=step[p]+1;
for(RI i=0;i<26;++i) ch[nq][i]=ch[q][i];
pre[nq]=pre[q],pre[q]=pre[np]=nq;
while(ch[p][x]==q) ch[p][x]=nq,p=pre[p];
}
}
}
void add(int x,int y) {to[++tot]=y,ne[tot]=h[x],h[x]=tot;}
void dfs(int x,int las) {
in[x]=++tim,f[x][0]=las;
for(RI i=1;i<=19;++i) f[x][i]=f[f[x][i-1]][i-1];
for(RI i=h[x];i;i=ne[i]) if(to[i]!=las) dfs(to[i],x);
out[x]=tim;
}
void chan(int x,int s,int t,int i,int v) {
if(s==t) {mx[i]=max(mx[i],v);return;}
int mid=(s+t)>>1;
if(x<=mid) chan(x,s,mid,i<<1,v);
else chan(x,mid+1,t,(i<<1)|1,v);
mx[i]=max(mx[i<<1],mx[(i<<1)|1]);
}
int query(int l,int r,int s,int t,int i) {
if(!l) return 0;
if(l<=s&&t<=r) return mx[i];
int mid=(s+t)>>1,re=0;
if(l<=mid) re=query(l,r,s,mid,i<<1);
if(mid+1<=r) re=max(re,query(l,r,mid+1,t,(i<<1)|1));
return re;
}
int getf(int x,int d) {
for(RI i=19;i>=0;--i) if(step[f[x][i]]>=d) x=f[x][i];
return x;
}
int check(int id) {
int x=getf(pos[id],dp[id]-1),y=getf(pos[id-1],dp[id]-1);
return query(in[x],out[x],1,tim,1)>=dp[id]-1||
query(in[y],out[y],1,tim,1)>=dp[id]-1;
}
int main()
{
scanf("%d",&n),scanf("%s",S+1);
reverse(S+1,S+1+n);
last=SZ=1;for(RI i=1;i<=n;++i) ins(S[i]-'a',i);
for(RI i=2;i<=SZ;++i) add(pre[i],i);
dfs(1,0);
for(RI i=1,j=0;i<=n;++i) {
dp[i]=dp[i-1]+1;
while(!check(i)) --dp[i],++j,chan(in[pos[j]],1,tim,1,dp[j]);
ans=max(ans,dp[i]);
}
printf("%d\n",ans);
return 0;
}