BZOJ 4310 跳蚤
首先我们知道我们要求的是使得最大值最小,显然是要二分的
我们先对原串建出后缀自动机
之后二分答案是第k小的字符串
对于答案可行性的判定:
我们注意到对于每一个区间,其字典序最大的子串一定是区间的某个后缀
那么我们不妨从后往前扫,这样每次只会增加一个后缀
我们只需要判断这个后缀是否比当前答案小就可以了
如果比当前答案大,就划分出一组
可以证明,这样分组是在满足条件的情况下分组最少的
至于两个串比较大小,可以用哈希做到O(logn)
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #include<cstdlib> using namespace std; typedef unsigned long long LL; const int maxn=400010; const int x=13331; int k,n,clen; char s[maxn]; char c[maxn]; bool vis[maxn]; LL dp[maxn]; LL xp[maxn],h[maxn],H[maxn]; int cnt=0,la=0; struct Node{ int next[26]; int len,link; }st[maxn]; void init(){ cnt=la=0; st[0].link=-1; } void add(int c){ int cur=++cnt; st[cur].len=st[la].len+1; int p; for(p=la;p!=-1&&st[p].next[c]==0;p=st[p].link)st[p].next[c]=cur; if(p==-1)st[cur].link=0; else{ int q=st[p].next[c]; if(st[q].len==st[p].len+1)st[cur].link=q; else{ int clone=++cnt; st[clone]=st[q]; st[clone].len=st[p].len+1; for(;p!=-1&&st[p].next[c]==q;p=st[p].link)st[p].next[c]=clone; st[q].link=st[cur].link=clone; } }la=cur; } LL Go(int u){ if(vis[u])return dp[u]; vis[u]=true;dp[u]=1; for(int i=0;i<26;++i){ if(st[u].next[i])dp[u]+=Go(st[u].next[i]); }return dp[u]; } void Get_C(LL k){ int now=0; while(1){ for(int i=0;i<26;++i){ if(st[now].next[i]){ int v=st[now].next[i]; if(k>dp[v])k-=dp[v]; else {c[++clen]=i+'a';now=v;break;} } }k--; if(k==0)return; }return; } LL Hash_C(int L,int R){return H[L]-H[R+1]*xp[R-L+1];} LL Hash_S(int L,int R){return h[L]-h[R+1]*xp[R-L+1];} bool cmp(int a,int b){ if(c[1]<s[a])return false; else if(c[1]>s[a])return true; int L=a,R=min(L+clen-1,b); while(L<R){ int mid=L+((R-L+1)>>1); if(Hash_C(1,mid-a+1)!=Hash_S(a,mid))R=mid-1; else L=mid; }L++; if(L>b)return true; else if(L-a+1>clen)return false; if(c[L-a+1]<s[L])return false; else if(c[L-a+1]>s[L])return true; } bool check(){ int ans=0,p; for(int i=n;i;i=p){ p=i; while(p&&cmp(p,i))p--; if(p==i)return false; ans++; }return ans<=k; } int main(){ scanf("%d",&k); scanf("%s",s+1);n=strlen(s+1); init(); for(int i=1;i<=n;++i)add(s[i]-'a'); Go(0);xp[0]=1;h[n+1]=0; for(int i=1;i<=n;++i)xp[i]=xp[i-1]*x; for(int i=n;i>=1;--i)h[i]=h[i+1]*x+s[i]-'a'; LL L=1,R=dp[0]-1; while(L<R){ LL mid=(L+R)>>1; clen=0;Get_C(mid); H[clen+1]=0; for(int i=clen;i>=1;--i)H[i]=H[i+1]*x+c[i]-'a'; if(check())R=mid; else L=mid+1; } clen=0;Get_C(R); for(int i=1;i<=clen;++i)printf("%c",c[i]); printf("\n");return 0; }