解析
好题。
首先,我们每次都令 sis_isi 是 si+1s_{i+1}si+1 的后缀,肯定是不劣的
问题就可以转化到 fail 树上了
首先肯定要线段树合并处理出endpos集合
朴素想法:设父亲 fafafa 的结束位置为 posfapos_{fa}posfa,若 [posfa−lenfa+1,posfa][pos_{fa}-len_{fa}+1,pos_{fa}][posfa−lenfa+1,posfa] 中有两个儿子 sss,就令 ansfa+1→anssans_{fa}+1\to ans_{s}ansfa+1→anss。
但是仔细想想还会有一些问题
比如,可能会跳两次才可以转移,但是连续的父子之间都无法构成二倍关系。这个不麻烦,记录一下上一次成功转移的 preprepre,每次判断 [pospre−lenpre+1,pospre][pos_{pre}-len_{pre}+1,pos_{pre}][pospre−lenpre+1,pospre] 即可
还有一个问题,我们这里的 lenlenlen 都是最长长度,会不会有一个同一等价类里较短的子串可以转移,而那个最长子串无法转移呢?
不会
考虑反证证明:
假设有子串:
- 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*/