题解 CF1063F 【String Journey】
首先将字符串翻转,相应地,有序串组也就翻转了,即为求最长的长度递增的有序串组,满足第 \(i\) 个串是第 \(i+1\) 个串的子串。进行贪心,发现最优秀情况下有序串组中的串长一定是逐渐加 \(1\) 的,即长度为 \(1,2,3,\dots\),因为可以对不是逐渐加 \(1\) 的位置删去一些字符,来使之后能匹配更多,所以这样是最优的。
考虑 \(DP\),设 \(dp_i\) 为最后一个字符串在 \(i\) 位置结束的最长有序串组的长度。有序串组的长度是具有单调性的,可以二分。二分长度 \(lenth\),因为串长是逐渐加 \(1\) 的,所以上一个串一定是当前串删去开头字符或删去结尾字符形成的,那么就是找 \(s[i-lenth+1,i]\) 和 \(s[i-lenth,i-1]\) 在之前的出现位置,根据结尾位置的 \(dp\) 值是否大于等于 \(dp_{i}-1\) 判定即可。出现位置可以通过在 \(SAM\) 的 \(Parent\) 树上查询子树,用线段树来进行维护最大值。
发现有 \(dp_i -1 \leqslant dp_{i-1}\),因为一定存在一种 \(dp_{i-1}\) 的方案可以用 \(dp_i\) 的方案里每个串删去一个字符来表示出来,所以可以不用二分,直接枚举即可,\(dp_i\) 每次最多加 \(1\),得枚举复杂度为 \(O(n)\)。发现 \(i-lenth\) 是单调递增的,所以可以用一个指针来维护判定的范围。
#include<bits/stdc++.h>
#define maxn 1000010
#define maxm 4000010
#define ls (cur<<1)
#define rs (cur<<1|1)
#define mid ((l+r)>>1)
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,ans,tot=1,las=1,root=1,cnt,pos;
int fa[maxn],len[maxn],ch[maxn][26],bel[maxn],dp[maxn];
int in[maxn],out[maxn],f[maxn][22],mx[maxm];
char s[maxn];
struct edge
{
int to,nxt;
}e[maxn];
int head[maxn],edge_cnt;
void add(int from,int to)
{
e[++edge_cnt]={to,head[from]},head[from]=edge_cnt;
}
void modify(int l,int r,int pos,int v,int cur)
{
if(l==r)
{
mx[cur]=v;
return;
}
if(pos<=mid) modify(l,mid,pos,v,ls);
else modify(mid+1,r,pos,v,rs);
mx[cur]=max(mx[ls],mx[rs]);
}
int query(int L,int R,int l,int r,int cur)
{
if(!L||!R) return 0;
if(L<=l&&R>=r) return mx[cur];
int v=0;
if(L<=mid) v=max(v,query(L,R,l,mid,ls));
if(R>mid) v=max(v,query(L,R,mid+1,r,rs));
return v;
}
void insert(int c,int id)
{
int p=las,np=las=++tot;
len[np]=len[p]+1,bel[id]=np;
while(p&&!ch[p][c]) ch[p][c]=np,p=fa[p];
if(!p) fa[np]=root;
else
{
int q=ch[p][c];
if(len[q]==len[p]+1) fa[np]=q;
else
{
int nq=++tot;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
len[nq]=len[p]+1,fa[nq]=fa[q],fa[np]=fa[q]=nq;
while(ch[p][c]==q) ch[p][c]=nq,p=fa[p];
}
}
}
void dfs(int x)
{
in[x]=++cnt,f[x][0]=fa[x];
for(int i=1;i<=20;++i) f[x][i]=f[f[x][i-1]][i-1];
for(int i=head[x];i;i=e[i].nxt) dfs(e[i].to);
out[x]=cnt;
}
int get(int x,int lenth)
{
x=bel[x];
for(int i=20;i>=0;--i)
if(f[x][i]&&len[f[x][i]]>=lenth)
x=f[x][i];
return x;
}
bool check(int p)
{
int x=get(p,dp[p]-1),y=get(p-1,dp[p]-1);
return max(query(in[x],out[x],1,tot,root),query(in[y],out[y],1,tot,root))>=dp[p]-1;
}
int main()
{
read(n),scanf("%s",s+1),reverse(s+1,s+n+1);
for(int i=1;i<=n;++i) insert(s[i]-'a',i);
for(int i=2;i<=tot;++i) add(fa[i],i);
dfs(root);
for(int i=1;i<=n;++i)
{
dp[i]=dp[i-1]+1;
while(!check(i))
dp[i]--,pos++,modify(1,tot,in[bel[pos]],dp[pos],root);
ans=max(ans,dp[i]);
}
printf("%d",ans);
return 0;
}