【题解】bzoj4310跳蚤(SA)

【题解】bzoj4310跳蚤(SA)

cao 还有二分字典序这种操作。。。。

问题性质显然满足二分性,但是我们只能对数进行二分,但是如果可以根据一个数确定一个字符串就好了。

用SA可以实现,根据一个整数x,可以在\(O(\min (n,ans))\),得到字典序在所有子串中x位的那个串具体是什么。

二分完之后就是贪心了,从后往前枚举每个位置要不要断开,现在问题就变成了一个母串中有两个子串要比较字典序,也是SA+倍增就行了。

贪心时可以发现,直接在当前位置的后面断开最优,因为这样分出来的字典序更小。

//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;

void Suf_sort(char*str,int*rk,int*sa,int*h,int n){
	static int temp[maxn<<1],b[maxn];
	for(int k=0,m=127;(1<<k>>1)<=n;++k){
		if(!k) for(int t=1;t<=n;++t) temp[t]=t,rk[t]=str[t],temp[t+n]=0;
		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) ++b[rk[t]];
		for(int t=1;t<=m;++t) b[t]+=b[t-1];
		for(int t=n;t;--t) sa[b[rk[temp[t]]]--]=temp[t];
		memset(b+1,0,m<<2); memcpy(temp+1,rk+1,n<<2);
		rk[sa[1]]=1;
		for(int t=2;t<=n;++t)
			rk[sa[t]]=temp[sa[t]]==temp[sa[t-1]]&&temp[sa[t]+l]==temp[sa[t-1]+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) p=0;
		else while(str[t+p]==str[sa[rk[t]-1]+p]) ++p;
		h[rk[t]]=p;//here
	}
}

int rk[maxn],sa[maxn],st[21][maxn],lg[maxn],n,k;
char c[maxn];
pair<int,int> getKth(ll k){
	for(int t=1;t<=n;++t)
		if(k>n-sa[t]-st[0][t]+1) k-=n-sa[t]-st[0][t]+1;
		else return make_pair(sa[t],sa[t]+k+st[0][t]-1);
	return make_pair(-1,-1);
}

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]);}
int lcp(int p,int q){if(rk[p]>rk[q])swap(p,q);return p==q?n-p+1:_que(rk[p]+1,rk[q]);}

bool isNotMore(pair<int,int> a,pair<int,int> b){//<=
	int lena=a.second-a.first+1,lenb=b.second-b.first+1;
	int p=min(min(lena,lenb),lcp(a.first,b.first));
	if(p==lena) return 1;
	if(p==lenb) return 0;
	return c[a.first+p]<=c[b.first+p];
}

bool chek(ll mid){
	pair<int,int> temp=getKth(mid);
	int ret=0;
	for(int t=n,r=n;t;--t){
		if(c[t]>c[temp.first]) return 0;
		if(!isNotMore(make_pair(t,r),temp)) r=t,++ret;
		if(ret>k) return 0;
	}
	return ret<=k;
}

int main(){
	scanf("%d%s",&k,c+1); n=strlen(c+1); --k;
	for(int t=2;t<=n;++t) lg[t]=lg[t>>1]+1;
	Suf_sort(c,rk,sa,st[0],n);
	for(int t=1;t<=lg[n];++t)
		for(int i=1;i<=n;++i)
			st[t][i]=min(st[t-1][i],st[t-1][i+(1<<t>>1)]);//here!!!!
	ll tol=(n+1ll)*n/2;
	for(int t=1;t<=n;++t) tol-=st[0][t];
	ll l=1,r=tol,mid;
	do
		if(!chek(mid=(l+r)>>1)) l=mid+1;
		else r=mid-1;
	while(l<=r);
	pair<int,int> s=getKth(r+1);
	cout<<string(c+s.first,c+s.second+1)<<endl;
	return 0;
}
posted @ 2020-02-20 09:22  谁是鸽王  阅读(160)  评论(0编辑  收藏  举报