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;
}

posted on 2020-02-21 11:48  autoint  阅读(109)  评论(0编辑  收藏  举报

导航