【题解】CF1063F String Journey(SA)
考虑最优解的形式,显然是(k,k-1,k-2dots 1)。
直觉告诉我们从后往前考虑,这样就只用考虑(k)到(k+1)的增量。设(dp(i,j)=0/1)且钦定第一个串是(S(i,i+j-1))是否存在"Journey"。
转移枚举一个(dp(k,j-1))然后判断一下(S(k,k+j-1+1))是否是(S(i,i+j-1))的子串即可。注意到因为长度差(=1),可以分别枚举(S(i,i+j-1))长度为(j-1)的前缀和后缀判等就行。
容易发现固定(i),(dp(i,j)=1)的(j)是一段前缀。那么只要记最大的那个就行了,记为(dp(i))(和前面的定义共存)。
转移要枚举一个(j)复杂度还是(ge O(n^2)),但我们还能发现,(dp(i)le dp(i+1)+1)。那么枚举(j)的复杂度均摊到(O(2n))。
这个转移可以用SA优化,因为SA数组上可以转移而来的(满足子串限制的)是一段区间,用线段树维护下(dp(i))的最大值就行。
但是我们忽略了一个要求是(k>t+j-1)。本来很难处理,但由于这里(dp(i)le dp(i+1)+1),也就是说(i+1)合法的(k)恰好(i)也合法,而且我们从大到小枚举的(k),所以加入过的(dp(k))不需要撤销,线段树搞搞就行了。
//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std; typedef long long ll;
const int maxn=5e5+5;
char s[maxn];
int n,lg[maxn],st[20][maxn],sa[maxn],rk[maxn],dp[maxn],seg[maxn<<2];
void sufsort(char*str,int*sa,int*rk,int*h,int n){
static int temp[maxn<<1],buk[maxn];
for(int k=0,m=128;(1<<k>>1)<=n;++k){
if(!k) for(int t=1;t<=n;++t) temp[t]=t,temp[t+n]=0,rk[t]=str[t];
int l=1<<k>>1,p=0,q=l;
for(int t=1;t<=n;++t){
if(sa[t]>n-l) temp[++p]=sa[t];
if(sa[t]>l) temp[++q]=sa[t]-l;
}
for(int t=1;t<=n;++t) ++buk[rk[t]];
for(int t=1;t<=m;++t) buk[t]+=buk[t-1];
for(int t=n;t;--t) sa[buk[rk[temp[t]]]--]=temp[t];
memcpy(temp+1,rk+1,n<<2); memset(buk+1,0,m<<2); rk[sa[1]]=1;
for(int t=2;t<=n;++t)
rk[sa[t]]=temp[sa[t-1]]==temp[sa[t]]&&temp[sa[t-1]+l]==temp[sa[t]+l]?rk[sa[t-1]]:rk[sa[t-1]]+1;
m=rk[sa[n]];
}
for(int t=1,p=0;t<=n;++t){
if(p) --p;
if(rk[t]!=1) while(str[t+p]==str[sa[rk[t]-1]+p]) ++p;
else p=0;
h[rk[t]]=p;
}
}
int que(int l,int r){
return min(st[lg[r-l+1]][l],st[lg[r-l+1]][r-(1<<lg[r-l+1])+1]);
}
#define mid ((l+r)>>1)
#define lef l,mid,pos<<1
#define rgt mid+1,r,pos<<1|1
int getL(int pos,int v){
if(st[0][pos]<v) return pos+1;
int l=1,r=pos-1;
do{
if(que(mid,pos)>=v) r=mid-1;
else l=mid+1;
}while(l<=r);
return l;
}
int getR(int pos,int v){
if(st[0][pos+1]<v) return pos-1;
int l=pos+1,r=n;
do{
if(que(pos+1,mid)>=v) l=mid+1;
else r=mid-1;
}while(l<=r);
return r;
}
void upd(int p,int v,int l,int r,int pos){
if(p<l||p>r) return;
if(l==r) return seg[pos]=v,void();
if(p<=mid) upd(p,v,lef);
if(mid<p) upd(p,v,rgt);
seg[pos]=max(seg[pos<<1],seg[pos<<1|1]);
}
int que(int L,int R,int l,int r,int pos){
if(L>r||R<l) return 0;
if(L<=l&&r<=R) return seg[pos];
return max(que(L,R,lef),que(L,R,rgt));
}
#undef mid
#undef lef
#undef rgt
int main(){
cin>>n>>(s+1);
sufsort(s,sa,rk,st[0],n);
for(int t=2;t<=n;++t) lg[t]=lg[t>>1]+1;
for(int t=1;t<=lg[n];++t)
for(int i=1;i+(1<<t>>1)<=n;++i)
st[t][i]=min(st[t-1][i],st[t-1][i+(1<<t>>1)]);
int ans=dp[n]=1;
for(int t=n,j=2;t;--t,++j){
while(j){
if(t+j<=n) upd(rk[t+j],dp[t+j],1,n,1);
if(que(getL(rk[t],j-1)-1,getR(rk[t],j-1),1,n,1)>=j-1) break;
if(que(getL(rk[t+1],j-1)-1,getR(rk[t+1],j-1),1,n,1)>=j-1) break;
--j;
}
ans=max(dp[t]=j,ans);
}
cout<<ans<<endl;
return 0;
}