[BZOJ4310] 跳蚤

Description

首先,他会把串分成不超过 \(k\) 个子串,然后对于每个子串 \(S\) ,他会从 \(S\) 的所有子串中选择字典序最大的那一个,并在选出来的 \(k\) 个子串中选择字典序最大的那一个。他称其为“魔力串”。现在他想找一个最优的分法让“魔力串”字典序最小。

Solution

我们将某个子串在所有子串(本质不同)中按照字典序的排名称作它的字典序排名。思考如何使用后缀数组快速求出一个子串的字典序排名。

假设我们要求 \(s[l,r]\) 这个子串的字典序排名,我们要在后缀排序中,从 \(r[l]\) 位置开始,向左找到第一个 \(h[i]<r-l+1\) 的位置,这个过程可以用二分 + ST表完成,那么 \(ra[i]-(n-sa[i]+1)+(r-l+1)\) 就是它的排名。

二分最终魔力串的字典序排名。我们倒着将整个串的字符一个个从前面插入,维护当前串的排名,如果当前串排名大于魔力串排名,那么这个当前串就危险了,我们要从它的第一个字符后面切开一刀。

这样我们只需要比较切的次数和 \(k\) 的关系,就可以调整二分边界。时间复杂度 \(O(nlogn)\)

我可真是个奥比啊 输出个方案挂了十几发

#include <bits/stdc++.h>
using namespace std; #define int long long
const int N = 200005; int n,m=256,sa[N],y[N],u[N],v[N],o[N],r[N],h[N],T,ra[N],Log2[N],lim,k,tbuf[N];
char str[N]; int rc=0; struct St {
int a[N][21];
void build(int *src,int n) {
for(int i=1;i<=n;i++) a[i][0]=src[i];
for(int i=1;i<=20;i++)
for(int j=1;j<=n-(1<<i)+1;j++)
a[j][i]=min(a[j][i-1],a[j+(1<<(i-1))][i-1]);
}
inline int query(int l,int r) {
int j=Log2[r-l+1];
return min(a[l][j],a[r-(1<<j)+1][j]);
}
} st; void generate_rank() {
for(int i=1;i<=n;i++) {
ra[i]=ra[i-1]+(n-sa[i]+1)-h[i];
}
} inline int bound(int l,int r,int v) {
while(r>l) {
int mid=(l+r)>>1;
if(st.query(mid+1,r)<v) l=mid+1;
else r=mid;;
}
return l;
} inline int getrank(int L,int R) {
int i=bound(1,r[L],R-L+1);
return ra[i]-(n-sa[i]+1)+(R-L+1);
} signed main(){ //freopen("inp.txt","r",stdin);
memset(str,0,sizeof str);
scanf("%lld%s",&k,str+1);
n=strlen(str+1); for(int i=1;i<=n;i++) Log2[i]=log2(i); for(int i=1;i<=n;i++) u[str[i]]++;
for(int i=1;i<=m;i++) u[i]+=u[i-1];
for(int i=n;i>=1;i--) sa[u[str[i]]--]=i;
r[sa[1]]=1;
for(int i=2;i<=n;i++) r[sa[i]]=r[sa[i-1]]+(str[sa[i]]!=str[sa[i-1]]); for(int l=1;r[sa[n]]<n;l<<=1) {
memset(u,0,sizeof u);
memset(v,0,sizeof v);
memcpy(o,r,sizeof r);
for(int i=1;i<=n;i++) u[r[i]]++, v[r[i+l]]++;
for(int i=1;i<=n;i++) u[i]+=u[i-1], v[i]+=v[i-1];
for(int i=n;i>=1;i--) y[v[r[i+l]]--]=i;
for(int i=n;i>=1;i--) sa[u[r[y[i]]]--]=y[i];
r[sa[1]]=1;
for(int i=2;i<=n;i++) r[sa[i]]=r[sa[i-1]]+((o[sa[i]]!=o[sa[i-1]])||(o[sa[i]+l]!=o[sa[i-1]+l]));
}
{
int i,j,k=0;
for(int i=1;i<=n;h[r[i++]]=k)
for(k?k--:0,j=sa[r[i]-1];str[i+k]==str[j+k];k++);
} st.build(h,n); generate_rank(); lim=n*(n+1)/2;
for(int i=1;i<=n;i++) lim-=h[i]; int l=1,r=ra[n],al,aw;
while(r>l) {
int mid=(l+r)/2,se=n,cnt=0,tl,tw,tx=0;
for(int i=n;i>=1;--i) {
if(getrank(i,se)>mid) {
if(i==se) {
cnt=INT_MAX;
break;
}
++cnt;
se=i;
}
if(cnt>k) break;
}
++cnt;
if(cnt<=k) r=mid;
else l=mid+1;
}
ra[n+1]=INT_MAX;
for(int i=1;i<=n;i++) {
if(n-sa[i]+1 - h[i] >= l) {
for(int j=sa[i]; j<=sa[i]+h[i]+l-1; j++) printf("%c",str[j]);
return 0;
}
else l-=n-sa[i]+1-h[i];
}
}

[BZOJ4310] 跳蚤 - 后缀数组,二分,ST表的更多相关文章

  1. BZOJ 3230 相似子串 | 后缀数组 二分 ST表

    BZOJ 3230 相似子串 题面 题解 首先我们要知道询问的两个子串的位置. 先正常跑一遍后缀数组并求出height数组. 对于每一个后缀suffix(i),考虑以i开头的子串有多少是之前没有出现过 ...

  2. BZOJ3230 相似子串[后缀数组+二分+st表]

    BZOJ3230 相似子串 给一个串,查询排名i和j的子串longest common suffix和longest common prefix 思路其实还是蛮好想的,就是码起来有点恶心.可以发现后缀 ...

  3. 【bzoj4310】跳蚤 后缀数组+二分

    题目描述 很久很久以前,森林里住着一群跳蚤.一天,跳蚤国王得到了一个神秘的字符串,它想进行研究. 首先,他会把串分成不超过 k 个子串,然后对于每个子串 S,他会从S的所有子串中选择字典序最大的那一个 ...

  4. bzoj 4310 跳蚤 —— 后缀数组+二分答案+贪心

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4310 二分答案——在本质不同的子串中二分答案! 如果二分到的子串位置是 st,考虑何时必须分 ...

  5. bzoj 4310 跳蚤——后缀数组+二分答案+贪心

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4310 答案有单调性? 二分出来一个子串,判断的时候需要满足那些字典序比它大的子串都不出现! ...

  6. UOJ #219 BZOJ 4650 luogu P1117 [NOI2016]优秀的拆分 (后缀数组、ST表)

    连NOI Day1T1都不会做...看了题解都写不出来还要抄Claris的代码.. 题目链接: (luogu)https://www.luogu.org/problemnew/show/P1117 ( ...

  7. [BZOJ3277/BZOJ3473] 串 - 后缀数组,二分,双指针,ST表,均摊分析

    [BZOJ3277] 串 Description 现在给定你n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串(注意包括本身). Solution 首先将所有串连 ...

  8. BZOJ4556:[TJOI\HEOI2016]字符串(后缀数组,主席树,二分,ST表)

    Description 佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物.生日礼物放在一个神奇的箱子中.箱子外边写了一个长为n的字符串s,和m个问题.佳媛姐姐必须正确回答这m个问题,才能打开箱 ...

  9. 【BZOJ3277/3473】串/字符串 后缀数组+二分+RMQ+双指针

    [BZOJ3277]串 Description 字符串是oi界常考的问题.现在给定你n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串(注意包括本身). Inpu ...

随机推荐

  1. python变量加逗号,的含义

    逗号,用于生成一个长度为1的元组 >>> (1) 1 >>> (1,) (1,) >>> 1, (1,) 因此需要将长度为1的元组中元素提取出来可 ...

  2. JN_0017:在当前目录运行CMD

    运行npm的时候,每次都要cd到目录,很麻烦,所以总结了三种在当前目录下直接打开cmd窗口的方法,供以后开发时参考,相信对其他人也有用. 方法一 在当前目录按住shift再右键. 会看到右键菜单里有一 ...

  3. Android之碎片Fragment

    Fragment是个特别的存在,有点像报纸上的专栏,看起来只占据页面的一小块,但是这一小块有自己的生命周期,可以自行其是,仿佛独立王国,并且这一小块的特性无论在哪个页面,给一个位置就行,添加以后不影响 ...

  4. 2020 CCPC Wannafly Winter Camp Day1 Div.1&amp F

    #include<bits/stdc++.h> #define forn(i, n) for (int i = 0; i < int(n); i++) #define fore(i, ...

  5. 使用Office365账号配置SMTP中继服务器

    · 如何将企业中的多功能设备(打印机.扫描仪等 )或应用程序设置为使用 Office 365 发送电子邮件,微软给出了3种方法: SMTP 客户端提交 直接发送 SMTP 中继 以上3种方式的介绍,大 ...

  6. Docker常用命令和功能介绍

    可以搜索 dockerfile 定制创建一个redis镜像image 表示镜像docker search 搜索镜像的名称和标签docker 所在目录/var/lib/dockerdocker的镜像文件 ...

  7. PAT (Advanced Level) Practice 1006 Sign In and Sign Out (25 分) (排序)

    At the beginning of every day, the first person who signs in the computer room will unlock the door, ...

  8. PAT (Advanced Level) Practice 1008 Elevator (20 分) (模拟)

    The highest building in our city has only one elevator. A request list is made up with N positive nu ...

  9. easyUI footer 的格式渲染

    网上好多的例子,但是自己使用的情况下还是出现bug.比如以下代码: var myview = $.extend({}, $.fn.datagrid.defaults.view, { renderFoo ...

  10. 13:IO流

    IO简介 继承结构 整体架构 常用内容 分类 根据处理的数据单位不同,分为字节流和字符流:in/out相对于程序而言的输入(读取)和输出(写出)的过程,即根据数据的流向不同称为输入流和输出流 字符流的 ...