CF1063F String Journey
Link
我们可以发现最后选出来的串的长度一定是\(ans,\cdots,1\)。
因为若连续的两个串长度为\(i+2,i\),那么一定可以通过把长度为\(i+2\)的串删除首/尾的字符使得长度连续且答案不劣。
设\(f_i\)表示强制\(str_i\)为某个答案串的开头时\(suf(i)\)的答案。
考虑枚举后一个答案串的开头\(j\),那么\(f_i=\max\{f_j|j-f_j>i\wedge sub(j,j+f_j-1)\subseteq sub(i,i+f_j)\}+1\)。
我们知道\(f_i\le f_{i+1}+1\),因此我们可以枚举\(f_i\)进行检查,最多检查\(O(n)\)次。
我们知道\(j\ge i+f_i\),而\(i+f_i\)不增,因此\(j\)不增,双指针维护合法的\(j\)区间即可。(此处合法只考虑\(j-f_j>i\)的限制,忽略\(sub(j,j+f_j-1)\subseteq sub(i,i+f_j)\)的限制)
由于\(f_j>f_i\)也算合法,因此我们实际上需要保证的是\(sub(i,i+f_i-2)\)或\(sub(i+1,i+f_i-1)\)是某个\(sub(j,j+f_j-1)\)的前缀。
在反串SAM的parent上处理出倍增数组,然后dfs序+线段树随便维护一下就好了。
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
const int N=1000007;
char str[N];int cnt=1,now=1,trans[N][26],len[N],link[N],pos[N],fa[N][20],st[N],ed[N],f[N],t[4*N];
std::vector<int>e[N];
#define ls p<<1
#define rs p<<1|1
#define mid ((l+r)/2)
void update(int p,int l,int r,int x,int v)
{
if(t[p]=std::max(t[p],v),l==r) return ;
x<=mid? update(ls,l,mid,x,v):update(rs,mid+1,r,x,v);
}
int query(int p,int l,int r,int L,int R)
{
if(l>R||L>r||L>R) return 0;
if(L<=l&&r<=R) return t[p];
return std::max(query(ls,l,mid,L,R),query(rs,mid+1,r,L,R));
}
#undef ls
#undef rs
#undef mid
void extend(int c)
{
int p=now,q;now=++cnt,len[now]=len[p]+1,pos[len[now]]=now;
for(;p&&!trans[p][c];p=link[p]) trans[p][c]=now;
if(!p) return link[now]=1,void();
if(len[q=trans[p][c]]==len[p]+1) return link[now]=q,void();
link[++cnt]=link[q],memcpy(trans[cnt],trans[q],104),len[cnt]=len[p]+1,link[now]=link[q]=cnt,pos[cnt]=pos[q];
for(;trans[p][c]==q;p=link[p]) trans[p][c]=cnt;
}
void dfs(int u)
{
static int tim;st[u]=++tim,fa[u][0]=link[u];
for(int i=1;i<20;++i) fa[u][i]=fa[fa[u][i-1]][i-1];
for(int v:e[u]) dfs(v);
ed[u]=tim;
}
int jump(int x,int l)
{
x=pos[x];
for(int i=19;~i;--i) if(len[fa[x][i]]>=l) x=fa[x][i];
return x;
}
int check(int x,int l,int pos=0){return x>=l&&(l==1||(pos=jump(x,l-1),query(1,1,cnt,st[pos],ed[pos])+1>=l)||(pos=jump(x-1,l-1),query(1,1,cnt,st[pos],ed[pos])+1>=l));}
int main()
{
int n,ans=0;
scanf("%d%s",&n,str+1),std::reverse(str+1,str+n+1);
for(int i=1;i<=n;++i) extend(str[i]-'a');
for(int i=2;i<=cnt;++i) e[link[i]].push_back(i);
dfs(1);
for(int i=1,j=0;i<=n;++i)
{
f[i]=f[i-1]+1;
while(!check(i,f[i])) --f[i],++j,update(1,1,cnt,st[jump(j,f[j])],f[j]);
ans=std::max(ans,f[i]);
}
printf("%d",ans);
}