【题解】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;
}
博客保留所有权利,谢绝学步园、码迷等不在文首明显处显著标明转载来源的任何个人或组织进行转载!其他文明转载授权且欢迎!