题意:定义长度为$k$的journey为一个字符串序列$t_{1\cdots k}$,对$\forall i\gt1$满足$t_i$是$t_{i-1}$的严格子串,定义字符串$s$上的journey为$t_{1\cdots k}$在$s$中依次不重叠出现的journey,给定$s$,求$s$上的最长journey
首先,如果我们能找到长度为$k$的journey,那么一定存在一个长度为$k$的journey满足$|t_i|=|t_{i-1}|-1$,这个直接在原journey上删一些字符即可
然后有一条性质:如果一个长度为$k$的journey满足$t_1$从$s_i$开始,那么对$\forall j\leq k$,存在长度为$j$的journey满足$t_1$从$s_i$开始,这个同样可以在原journey中删字符得到
所以考虑DP:设$f_i$表示最长的journey使得$t_1$从$s_i$开始,枚举$t_1=s_{i\cdots i+k-1}$,得到转移$f_i=\max\left\{k|\exists j\geq i+k,f_j\geq k-1,s_{j\cdots j+k-2}\subset s_{i\cdots i+k-1}\right\}$
如果知道怎么解决后面的限制,那么我们可以用二分来求$f_i$,需要$O(n\log n)$次check,但因为$f_{i+1}\geq f_i-1$(如果$f_i$表示的journey满足$t_2$在$t_1$的开始匹配位置是$s_i$,那么$f_{i+1}$就可能$-1$),所以我们可以从$f_{i+1}+1$开始往下check,总共调用$O(n)$次check
现在来看怎么check,字符串的条件是说$s_{j\cdots j+k-2}=s_{i\cdots i+k-2}$或$s_{i+1\cdots i+k-1}$,而子串在后缀数组上对应着一个区间,所以我们可以把$f_i$赋到$rank_i$上,在线段树上查询区间最大值即可,还有一个条件$j\geq i+k$,但很容易发现满足条件的$j$其实是随着$i$减小而单调不增的($i$不变时$k$在不断减小,$i$减少$1$的同时$k$只能增加$1$),所以在线段树上动态插入即可,单次check的时间复杂度为$O(\log n)$
总时间复杂度为$O(n\log n)$,是很优美的复杂度
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
char s[500010];
int rk[1000010],sa[500010],c[500010],h[500010],mn[500010][19],lg[500010],n;
struct pr{
int c[2],i;
pr(int a=0,int b=0,int d=0){c[0]=a;c[1]=b;i=d;}
}p[500010],q[500010];
bool operator!=(pr a,pr b){return a.c[0]!=b.c[0]||a.c[1]!=b.c[1];}
void sort(int f){
int i,m;
memset(c,0,sizeof(c));
m=0;
for(i=1;i<=n;i++){
c[p[i].c[f]]++;
m=max(m,p[i].c[f]);
}
for(i=1;i<=m;i++)c[i]+=c[i-1];
for(i=n;i>0;i--)q[c[p[i].c[f]]--]=p[i];
memcpy(p,q,sizeof(q));
}
void suf(){
int i,j,l,m;
for(i=1;i<=n;i++)rk[i]=s[i]-'a'+1;
for(l=1;l<=n;l<<=1){
for(i=1;i<=n;i++)p[i]=pr(rk[i],rk[i+l],i);
sort(1);
sort(0);
for(i=1,m=0;i<=n;i++){
if(p[i]!=p[i-1])m++;
rk[p[i].i]=m;
}
}
for(i=1;i<=n;i++)sa[rk[i]]=i;
for(i=1,l=0;i<=n;i++){
if(l)l--;
while(s[i+l]==s[sa[rk[i]-1]+l])l++;
h[rk[i]]=l;
}
for(i=1;i<=n;i++)mn[i][0]=h[i];
for(j=1;j<19;j++){
for(i=1;i+(1<<j)-1<=n;i++)mn[i][j]=min(mn[i][j-1],mn[i+(1<<(j-1))][j-1]);
}
for(i=2;i<=n;i++)lg[i]=lg[i>>1]+1;
}
int qmin(int l,int r){
int k=lg[r-l+1];
return min(mn[l][k],mn[r-(1<<k)+1][k]);
}
int mx[2097152],M;
void modify(int x,int v){
if(!x)return;
x+=M;
mx[x]=max(mx[x],v);
for(x>>=1;x;x>>=1)mx[x]=max(mx[x<<1],mx[x<<1|1]);
}
int query(int s,int t){
int res=-1;
for(s+=M-1,t+=M+1;s^t^1;s>>=1,t>>=1){
if(~s&1)res=max(res,mx[s^1]);
if(t&1)res=max(res,mx[t^1]);
}
return res;
}
int submax(int s,int k){
int l,r,mid,ans,lp,rp;
if(k==0)return mx[1];
ans=s;
l=1;
r=s-1;
while(l<=r){
mid=(l+r)>>1;
if(qmin(mid+1,s)>=k){
ans=mid;
r=mid-1;
}else
l=mid+1;
}
lp=ans;
ans=s;
l=s+1;
r=n;
while(l<=r){
mid=(l+r)>>1;
if(qmin(s+1,mid)>=k){
ans=mid;
l=mid+1;
}else
r=mid-1;
}
rp=ans;
return query(lp,rp);
}
int f[500010];
int main(){
int i,j,ans;
scanf("%d%s",&n,s+1);
suf();
memset(mx,-1,sizeof(mx));
for(M=1;M<=n+2;M<<=1);
modify(n+1,0);
ans=0;
for(i=n;i>0;i--){
for(j=f[i+1]+1;;j--){
modify(rk[i+j],f[i+j]);
if(max(submax(rk[i],j-1),submax(rk[i+1],j-1))>=j-1)break;
}
f[i]=j;
ans=max(ans,f[i]);
}
printf("%d",ans);
}