BZOJ4310 跳蚤
跳蚤
给定一个字符串 \(S\),你需要将它分成不超过 \(m\) 个连续子串,使得分割后的所有串的子串中字典序最大的尽量小
\(|S| ≤ 10^5\)
题解
考虑二分答案,可以二分最大的那个子串,然后用后缀树求出。
从后往前,只要不需要切断,就不切断,切断的判断很容易用判 LCP 实现。
最后 check 断点是否不超过 m 个即可
时间复杂度 \(O(n \log n)\)。
CO int N=2e5+10,inf=1e9;
int last=1,tot=1;
int ch[N][26],fa[N],len[N],pos[N],idx[N];
void extend(int c,int p){
int x=last,cur=last=++tot;
len[cur]=len[x]+1,pos[cur]=p,idx[p]=cur;
for(;x and !ch[x][c];x=fa[x]) ch[x][c]=cur;
if(!x) {fa[cur]=1;return;};
int y=ch[x][c];
if(len[y]==len[x]+1) {fa[cur]=y;return;}
int clone=++tot;
copy(ch[y],ch[y]+26,ch[clone]);
fa[clone]=fa[y],len[clone]=len[x]+1,pos[clone]=pos[cur];
fa[cur]=fa[y]=clone;
for(;ch[x][c]==y;x=fa[x]) ch[x][c]=clone;
}
int son[N][26];
int sa[N];int64 sum[N];
int rnk[N],st[2*N][19],lg[2*N];
void dfs(int x){
sa[++sa[0]]=x,sum[sa[0]]=len[x]-len[fa[x]];
st[rnk[x]=++rnk[0]][0]=len[x];
for(int c=0;c<26;++c)if(son[x][c]){
dfs(son[x][c]);
st[++rnk[0]][0]=len[x];
}
}
int lcp(int i,int j){
i=rnk[idx[i]],j=rnk[idx[j]];
if(i>j) swap(i,j);
int k=lg[j-i+1];
return min(st[i][k],st[j-(1<<k)+1][k]);
}
pair<int,int> search(int64 lim){
int l=1,r=sa[0];
while(l<r){
int mid=(l+r+1)>>1;
if(sum[mid]<lim) l=mid;
else r=mid-1;
}
lim-=sum[l];
int x=sa[l+1];
return {pos[x],pos[x]+len[fa[x]]+lim-1};
}
char str[N],maxc;
int solve(int n,CO pair<int,int>&x){
if(str[x.first]!=str[0]) return inf;
int ans=0;
for(int l=n,r=n;l>=1;--l){
int len=min(lcp(l,x.first),min(r-l+1,x.second-x.first+1));
char a=len<r-l+1?str[l+len]:0;
char b=len<x.second-x.first+1?str[x.first+len]:0;
if(a>b) ++ans,r=l;
}
return ++ans;
}
int main(){
int K=read<int>();
scanf("%s",str+1);
int n=strlen(str+1);
for(int i=n;i>=1;--i) extend(str[i]-'a',i);
for(int i=2;i<=tot;++i)
son[fa[i]][str[pos[i]+len[fa[i]]]-'a']=i;
dfs(1);
for(int i=2;i<=sa[0];++i) sum[i]+=sum[i-1];
for(int i=2;i<=rnk[0];++i) lg[i]=lg[i>>1]+1;
for(int j=1;j<=lg[rnk[0]];++j)for(int i=1;i+(1<<j)-1<=rnk[0];++i)
st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
for(int i=1;i<=n;++i) str[0]=max(str[0],str[i]);
int64 l=1,r=sum[sa[0]];
while(l<r){
int64 mid=(l+r)>>1;
if(solve(n,search(mid))<=K) r=mid;
else l=mid+1;
}
pair<int,int> x=search(l);
for(int i=x.first;i<=x.second;++i) putchar(str[i]);
return 0;
}
静渊以有谋,疏通而知事。