【BZOJ4310】—跳蚤(后缀数组+二分答案)

传送门

darkbzojdarkbzoj上题面有误,应该是最大的最小

考虑二分这个最小的串xx的排名kk
那么也就是说所有排名比这个大的一定要被切开

考虑一个后缀S[p,n]S[p,n]
如果lcp(S[p,n],x)=0lcp(S[p,n],x)=0那么显然不合法
否则考虑在lcplcp前切开

考虑从后往前考虑每个后缀
那么显然每次在p+1p+1切开一定就是最优的

#include<bits/stdc++.h>
using namespace std;
const int RLEN=1<<20|1;
inline char gc(){
    static char ibuf[RLEN],*ib,*ob;
    (ob==ib)&&(ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
    return (ob==ib)?EOF:*ib++;
}
#define gc getchar
inline int read(){
    char ch=gc();
    int res=0,f=1;
    while(!isdigit(ch))f^=ch=='-',ch=gc();
    while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=gc();
    return f?res:-res;
}
#define ll long long
#define re register
#define pii pair<int,int>
#define pic pair<int,char>
#define fi first
#define se second
#define pb push_back
#define cs const
#define bg begin
#define poly vector<int>
#define chemx(a,b) ((a)<(b)?(a)=(b):0)
#define chemn(a,b) ((a)>(b)?(a)=(b):0)
cs int N=100005;
namespace Sa{
	int n,m,rk[N],sa[N],cnt[N],sa2[N],ht[N],s[N];
	inline void buc_sort(){
		for(int i=1;i<=m;i++)cnt[i]=0;
		for(int i=1;i<=n;i++)cnt[rk[sa2[i]]]++;
		for(int i=1;i<=m;i++)cnt[i]+=cnt[i-1];
		for(int i=n;i>=1;i--)sa[cnt[rk[sa2[i]]]--]=sa2[i];
	}
	inline void buildsa(char *a){
		n=strlen(a+1),m=30;
		for(int i=1;i<=n;i++)s[i]=a[i]-'a'+1;
		for(int i=1;i<=n;i++)rk[i]=s[i],sa2[i]=i;
		buc_sort();
		for(int pos=0,i=1;i<=n&&pos<n;i<<=1){
			pos=0;
			for(int j=n-i+1;j<=n;j++)sa2[++pos]=j;
			for(int j=1;j<=n;j++)if(sa[j]>i)sa2[++pos]=sa[j]-i;
			buc_sort();
			swap(rk,sa2);
			rk[sa[1]]=1,pos=1;
			for(int j=2;j<=n;j++)
			rk[sa[j]]=(sa2[sa[j-1]]==sa2[sa[j]]&&sa2[sa[j-1]+i]==sa2[sa[j]+i])?pos:++pos;
			m=pos;
		}
		for(int i=1,j,k=0;i<=n;ht[rk[i++]]=k)
		for(k?k--:0,j=sa[rk[i]-1];s[j+k]==s[i+k];k++);
	}
	int lg[N],st[20][N<<1];
	inline void buildst(){
		for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;
		for(int i=2;i<=n;i++)st[0][i]=ht[i];
		for(int i=1;(1<<i)<=n;i++)
		for(int j=1;j+(1<<i)-1<=n;j++)
		st[i][j]=min(st[i-1][j],st[i-1][j+(1<<(i-1))]);
	}
	inline int Lcp(int x,int y){
		if(x==y)return n-x+1;
		x=rk[x],y=rk[y];
		if(x>y)swap(x,y);x++;
		int t=lg[y-x+1];
		return min(st[t][x],st[t][y-(1<<t)+1]);
	}
	inline ll calc(){
		ll res=0;
		for(int i=1;i<=n;i++)res+=n-sa[i]+1-ht[i];
		return res;
	}
	inline pii getkth(ll k){
		ll res=0;
		for(int i=1;i<=n;i++){
			if(res<k&&res+n-sa[i]+1-ht[i]>=k){
				pii now;
				now.fi=sa[i];
				now.se=k-res+sa[i]+ht[i]-1;
				return now;
			}
			res+=n-sa[i]+1-ht[i];
		}
	}
	inline bool comp(int l1,int r1,int l2,int r2){
		int len=min(r1-l1+1,r2-l2+1);
		int lcp=Lcp(l1,l2);
		if(lcp<len)return s[l1+lcp]<s[l2+lcp];
		return r1-l1+1<r2-l2+1;
	}
	inline bool check(ll k,int tim){
		pii now=getkth(k);int last=n+1;
		for(int i=n;i;i--){
			if(comp(i,n,now.fi,now.se))continue;
			int lcp=min(now.se-now.fi+1,Lcp(now.fi,i));
			if(lcp==0)return false;
			if(i+lcp<last)tim--,last=i+1;
		}
		if(last>1)tim--;
		if(tim<0)return false;
		return true;
	}
	inline void solve(int k){
		ll l=1,r=calc(),res=r;
		while(l<=r){
			ll mid=(l+r)>>1;
			if(check(mid,k))r=mid-1,res=mid;
			else l=mid+1;
		}
		pii now=getkth(res);
		for(int i=now.fi;i<=now.se;i++)putchar(s[i]+'a'-1);
	}
}
int n,k;
char s[N];
int main(){
	k=read();
	scanf("%s",s+1);
	n=strlen(s+1);
	Sa::buildsa(s);
	Sa::buildst();
	Sa::solve(k);
}
原文地址:https://www.cnblogs.com/stargazer-cyk/p/12328443.html