bzoj 4310: 跳蚤 (后缀数组+二分+ST表)
来源:互联网 发布:java虚拟机占用内存 编辑:程序博客网 时间:2024/05/29 12:13
题目描述
传送门
题目大意:有一个长度为 n n n 的字符串, 你需要把它分成不超过k 段, 设第 i 段的字典序最大的子串为
题解
看到最小值最大,比较容易想到的思路就是二分。
对于字符串建立后缀数组,字符串中所有的本质不同的子串的个数是
我们可以二分子串,判断是否可行。首先用与统计类似的方式找到第mid个子串[l,r],然后对字符串贪心的进行划分。从后向前,如果[i,last]的字典序大于[l,r],那么i后面就需要划分一刀,如果最后划分成的数量超过k,那么就不能满足。需要特别注意,如果s[i]>s[l],那么直接判断不能满足。
对于判断两个串的字典序大小,先用st表找出两个串的lcp,然后分类讨论一下就可以了。
需要特别注意的是查询的时候如果x=y,那么需要特判。
代码
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#define N 600003#define LL long long using namespace std;int n,a[N],sa[N],*y,*x,xx[N],yy[N],rank[N],st[N][21],L[N],height[N],ls,rs,ansl,ansr,K,v[N];char s[N];int cmp(int i,int j,int k){ return y[i]==y[j]&&(i+k>n?-1:y[i+k])==(j+k>n?-1:y[j+k]);}void get_SA(){ x=xx; y=yy; int m=30; int p; for (int i=1;i<=n;i++) v[x[i]=a[i]]++; for (int i=1;i<=m;i++) v[i]+=v[i-1]; for (int i=n;i>=1;i--) sa[v[x[i]]--]=i; for (int k=1;k<=n;k<<=1) { p=0; for (int i=n-k+1;i<=n;i++) y[++p]=i; for (int i=1;i<=n;i++) if (sa[i]>k) y[++p]=sa[i]-k; for (int i=0;i<=m;i++) v[i]=0; for (int i=1;i<=n;i++) v[x[y[i]]]++; for (int i=1;i<=m;i++) v[i]+=v[i-1]; for (int i=n;i>=1;i--) sa[v[x[y[i]]]--]=y[i]; swap(x,y); p=2; x[sa[1]]=1; for (int i=2;i<=n;i++) x[sa[i]]=(cmp(sa[i],sa[i-1],k)?p-1:p++); if (p>n) break; m=p+1; } for (int i=1;i<=n;i++) rank[sa[i]]=i; p=0; for (int i=1;i<=n;i++) { if (rank[i]==1) continue; int j=sa[rank[i]-1]; while (j+p<=n&&i+p<=n&&a[j+p]==a[i+p]) p++; height[rank[i]]=p; p=max(p-1,0); } for (int i=1;i<=n;i++) st[i][0]=height[i]; for (int i=1;i<=19;i++) for (int j=1;j<=n;j++) if (j+(1<<i)-1<=n) st[j][i]=min(st[j][i-1],st[j+(1<<i-1)][i-1]); int j=0; for (int i=1;i<=n;i++) { if ((1<<j+1)<=i) j++; L[i]=j; }}void get_kth(LL t){ for (int i=1;i<=n;i++) if (t>(n-height[i]-sa[i]+1)) t-=(n-height[i]-sa[i]+1); else { ls=sa[i]; rs=sa[i]+height[i]+t-1; return; }}int calc(int x,int y){ if (x==y) return n-sa[x]+1; if (x>y) swap(x,y); int k=L[y-x]; x++; return min(st[x][k],st[y-(1<<k)+1][k]);}bool compare(int l1,int r1,int l2,int r2){ int len1=r1-l1+1; int len2=r2-l2+1; int lcp=calc(rank[l1],rank[l2]); if (lcp>=len1) return len1<=len2; if (lcp>=len2) return 0; return a[l1+lcp]<=a[l2+lcp];}bool check(int mid){ int last=n; int cnt=1; for (int i=n;i>=1;i--){ if (a[i]>a[ls]) return 0; if (!compare(i,last,ls,rs)) { cnt++; last=i; if (cnt>K) return 0; } } return 1;}int main(){ scanf("%d",&K); scanf("%s",s+1); n=strlen(s+1); for (int i=1;i<=n;i++) a[i]=s[i]-'a'+1; get_SA(); LL l=1; LL r=0; for (int i=1;i<=n;i++) r+=(n-height[i]-sa[i]+1); while (l<=r) { LL mid=(l+r)/2; get_kth(mid); if (check(mid)) ansl=ls,ansr=rs,r=mid-1; else l=mid+1; } for (int i=ansl;i<=ansr;i++) printf("%c",s[i]); printf("\n");}
阅读全文
0 0
- bzoj 4310: 跳蚤 (后缀数组+二分+ST表)
- 【BZOJ4310】跳蚤,后缀数组+ST表求LCP+二分答案
- BZOJ4310 跳蚤(后缀数组+二分答案)
- BZOJ 3230 相似子串 后缀数组+二分+ST表
- bzoj 2534: Uva10829L-gap字符串 (后缀数组+ST表)
- [BZOJ3277]串(后缀数组+二分+st表)
- [BZOJ3230]相似子串(后缀数组+二分+st表)
- BZOJ 3230 后缀数组+ST
- BZOJ 4566 [Haoi2016]找相同字符 后缀数组+ST表
- BZOJ 3238 [Ahoi2013]差异 后缀数组+st表
- bzoj 2119: 股市的预测 (后缀数组+差分+st表)
- BZOJ 4516 后缀数组+ST+set
- [BZOJ4556][Tjoi2016&Heoi2016]字符串(后缀数组+二分+st表+主席树)
- bzoj 3521/5083 [Poi2014]Salad Bar/普及 (卡常)st表+二分+树状数组
- bzoj 3230: 相似子串 (后缀数组+RMQ+二分)
- bzoj 4698: Sdoi2008 Sandy的卡片 (后缀数组+二分)
- BZOJ 4310: 跳蚤
- POJ - 3882 Stammering Aliens 后缀数组、二分、二叉堆、ST表
- 虚拟化-传统-VMM
- POJ2431-C语言
- 微信 WCDB 正式开源——高效易用的移动数据库框架
- linux 常用命令
- ORACLE初学第二篇
- bzoj 4310: 跳蚤 (后缀数组+二分+ST表)
- Android中TextView属性全纪录
- Recyclerview使用体验(一)
- 关于SwipeRefreshLayout的刷新事件
- 关于Android适配,常用这几种就够了
- Android获取设备唯一标识
- - Unable to add window -- token null is not valid; is your activity running?
- Error:Execution failed for task ':app:transformNative_libsWithStripDebugSymbolForDebug'. > java.lang.NullPointerExcep
- win下的问题积累