解析
好题。
首先,我们每次都令
s
i
s_i
si 是
s
i
+
1
s_{i+1}
si+1 的后缀,肯定是不劣的
问题就可以转化到 fail 树上了
首先肯定要线段树合并处理出endpos集合
朴素想法:设父亲
f
a
fa
fa 的结束位置为
p
o
s
f
a
pos_{fa}
posfa,若
[
p
o
s
f
a
−
l
e
n
f
a
+
1
,
p
o
s
f
a
]
[pos_{fa}-len_{fa}+1,pos_{fa}]
[posfa−lenfa+1,posfa] 中有两个儿子
s
s
s,就令
a
n
s
f
a
+
1
→
a
n
s
s
ans_{fa}+1\to ans_{s}
ansfa+1→anss。
但是仔细想想还会有一些问题
比如,可能会跳两次才可以转移,但是连续的父子之间都无法构成二倍关系。这个不麻烦,记录一下上一次成功转移的
p
r
e
pre
pre,每次判断
[
p
o
s
p
r
e
−
l
e
n
p
r
e
+
1
,
p
o
s
p
r
e
]
[pos_{pre}-len_{pre}+1,pos_{pre}]
[pospre−lenpre+1,pospre] 即可
还有一个问题,我们这里的
l
e
n
len
len 都是最长长度,会不会有一个同一等价类里较短的子串可以转移,而那个最长子串无法转移呢?
不会
考虑反证证明:
假设有子串:
- A
- cA
- AA
其中1、2属于同一等价类,2无法转移到3而1可以
那么必然有串:cAA(这样12才能是一个等价类)
又由于3是该等价类最长字符串,所以AA和cAA不属于同一等价类
也就是说有一个位置出现了AA却没有出现cAA,那么这个地方(第一个A的位置)也会出现了A却没有出现cA
所以1、2不在一个等价类里,与题设矛盾,原命题得证
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=4e5+100;
const int mod=1e9+7;
inline ll read(){
ll x(0),f(1);char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
int n,m;
int ed[N];
struct SAM{
struct node{
int len,fa;
int tr[26];
}st[N];
int lst=1,tot=1;
int pl[N];
inline void ins(int c,int o){
c-='a';
int cur=++tot,p=lst;lst=tot;ed[cur]=o;
st[cur].len=st[p].len+1;
for(;p&&!st[p].tr[c];p=st[p].fa) st[p].tr[c]=cur;
if(!st[p].tr[c]) st[cur].fa=1;
else{
int q=st[p].tr[c];
if(st[q].len==st[p].len+1) st[cur].fa=q;
else{
int pp=++tot;st[pp]=st[q];
ed[pp]=ed[q];
st[pp].len=st[p].len+1;
st[cur].fa=st[q].fa=pp;
for(;p&&st[p].tr[c]==q;p=st[p].fa) st[p].tr[c]=pp;
}
}
}
void print(){
for(int i=1;i<=tot;i++){
printf("i=%d fa=%d len=%d ed=%d\n",i,st[i].fa,st[i].len,ed[i]);
}
putchar('\n');
return;
}
}s;
struct segmentTree{
#define mid ((l+r)>>1)
struct tree{
int ls,rs,siz;
}tr[N<<6];
int rt[N],tot;
inline int copy(int x){
tr[++tot]=tr[x];return tot;
}
inline void pushup(int k){
tr[k].siz=tr[tr[k].ls].siz+tr[tr[k].rs].siz;return;
}
void add(int &k,int l,int r,int p){
if(!k) k=copy(0);
if(l==r){
tr[k].siz++;return;
}
if(p<=mid) add(tr[k].ls,l,mid,p);
else add(tr[k].rs,mid+1,r,p);
pushup(k);
}
int merge(int a,int b,int l,int r){
if(!a||!b) return a|b;
a=copy(a);
if(l==r){
tr[a].siz+=tr[b].siz;return a;
}
tr[a].ls=merge(tr[a].ls,tr[b].ls,l,mid);
tr[a].rs=merge(tr[a].rs,tr[b].rs,mid+1,r);
pushup(a);
return a;
}
int ask(int k,int l,int r,int x,int y){
if(x>y) return 0;
if(!k) return 0;
if(x<=l&&r<=y) return tr[k].siz;
int res(0);
if(x<=mid) res+=ask(tr[k].ls,l,mid,x,y);
if(y>mid) res+=ask(tr[k].rs,mid+1,r,x,y);
return res;
}
}t;
char ss[N];
vector<int>v[N];
int dp[N],pre[N];
void dfs1(int x){
if(ed[x]) t.add(t.rt[x],1,n,ed[x]);
for(int to:v[x]){
//printf("%d -> %d\n",x,to);
dfs1(to);
t.rt[x]=t.merge(t.rt[x],t.rt[to],1,n);
}
return;
}
int ans;
void dfs2(int x){
//printf("x=%d dp=%d pre=%d\n",x,dp[x],pre[x]);
ans=max(ans,dp[x]);
for(int to:v[x]){
//printf(" %d -> %d +1? (%d %d)\n",x,to,ed[to]-s.st[to].len+s.st[pre[x]].len,ed[to]-1);
if(x==1||(ed[to]&&t.ask(t.rt[pre[x]],1,n,ed[to]-s.st[to].len+s.st[pre[x]].len,ed[to]-1))){
dp[to]=dp[x]+1;pre[to]=to;
}
else dp[to]=dp[x],pre[to]=pre[x];
dfs2(to);
}
return;
}
signed main() {
#ifndef ONLINE_JUDGE
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
#endif
//printf("%d\n",sizeof(t)/1024/1024);
n=read();
scanf(" %s",ss+1);
for(int i=1;i<=n;i++) s.ins(ss[i],i);
//s.print();
for(int i=2;i<=s.tot;i++) v[s.st[i].fa].push_back(i);
dfs1(1);
dfs2(1);
printf("%d\n",ans);
return 0;
}
/*
21
abcaaaaabbcbacbabcbcb
*/