【BZOJ4310】跳蚤

Description

  很久很久以前,森林里住着一群跳蚤。一天,跳蚤国王得到了一个神秘的字符串,它想进行研究。

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

  现在他想找一个最优的分法让“魔力串”字典序最小。

Input

  从文件flea.in中读入数据。

  第一行一个整数k。

  接下来一个长度不超过10^5的字符串 S。

Output

  输出到文件flea.out中。

  输出一行,表示字典序最小的“魔力串”。

Sample Input

13 bcbcbacbbbbbabbacbcbacbbababaabbbaabacacbbbccaccbcaabcacbacbcabaacbccbbcbcbacccbcccbbcaacabacaaaaaba

Sample Output

cbc

Hint

【数据规模和约定】

  对于30%的数据,S的长度<=100

  对于60%的数据,S的长度<=1000

  对于100%的数据,S的长度<=100000

后缀自动机+二分+Hash。

看到最大值最小,容易想到二分答案。

我们将原串建好后缀自动机,这样我们二分的就是字符串的排名。二分出一个\(ans\)过后,找到具有该排名的串在原串中的位置(这个很好找)。

然后就贪心验证。从后往前尽量加入字符串,如果不能加入就开一个新的子串。在最前面加入一个字符后相当于加入了一个前缀,所以我们比较的时候用二分+Hash找到第一个与答案串不同的位置,然后比较大小。

代码:

#include<bits/stdc++.h>
#define ll long long
#define N 200005
#define int ll using namespace std;
inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;} int K,n;
char s[N];
int mxlen[N<<1],fail[N<<1];
int ch[N<<1][26];
int cnt=1,last=1;
int pos[N<<1];
void Insert(int f,int i) {
static int p,now;
now=++cnt;
p=last,last=now;
pos[now]=i;
mxlen[now]=mxlen[p]+1;
while(p&&!ch[p][f]) ch[p][f]=now,p=fail[p];
if(!p) return fail[now]=1,void();
int q=ch[p][f];
if(mxlen[q]==mxlen[p]+1) return fail[now]=q,void();
int New=++cnt;
memcpy(ch[New],ch[q],sizeof(ch[q]));
fail[New]=fail[q];
fail[q]=fail[now]=New;
mxlen[New]=mxlen[p]+1;
while(p&&ch[p][f]==q) ch[p][f]=New,p=fail[p];
} int t[N<<1],q[N<<1];
ll size[N<<1];
void top_sort(int n) {
for(int i=1;i<=cnt;i++) t[mxlen[i]]++;
for(int i=1;i<=n;i++) t[i]+=t[i-1];
for(int i=1;i<=cnt;i++) q[t[mxlen[i]]--]=i;
for(int i=cnt;i>=1;i--) {
int v=q[i];
size[v]=1;
for(int j=0;j<26;j++)
if(ch[v][j]) size[v]+=size[ch[v][j]];
pos[fail[v]]=pos[v];
}
} const ll p=37,mod=1e9+7;
ll Hash[N],pw[N];
int ls,rs;
int ed,len; void Find_kth(int now,ll k) {
if(k==1&&now!=1) return ed=pos[now],void();
k-=(now!=1);
for(int i=0;i<26;i++) {
if(!ch[now][i]) continue ;
if(size[ch[now][i]]>=k) {
len++;
Find_kth(ch[now][i],k);
return ;
}
k-=size[ch[now][i]];
}
} ll Get_hash(int l,int r) {return (Hash[r]-Hash[l-1]*pw[r-l+1]%mod+mod)%mod;}
int same(int l1,int r1,int l2,int r2) {
if(s[l1]!=s[l2]) return 0;
int l=1,r=min(r1-l1+1,r2-l2+1),mid;
while(l<r) {
mid=l+r+1>>1;
if(Get_hash(l1,l1+mid-1)==Get_hash(l2,l2+mid-1)) l=mid;
else r=mid-1;
}
return l;
} bool low(int l1,int r1,int l2,int r2) {
int x=same(l1,r1,l2,r2);
if(x==r1-l1+1) return 1;
if(x==r2-l2+1) return 0;
return s[l1+x]<s[l2+x];
} bool solve(int ans) {
ed=0,len=0;
Find_kth(1,ans);
ls=ed-len+1,rs=ed;
int k=1;
int i,j;
for(i=n;i>=1;i--) {
for(j=i;j>=1;j--) {
if(!low(j,i,ls,rs)) {
if(i==j) return 0;
k++;
break;
}
}
i=j+1;
}
return k<=K;
} void pre() {
K=Get();
scanf("%s",s+1);
n=strlen(s+1);
pw[0]=1;
for(int i=1;i<=n;i++) pw[i]=pw[i-1]*p%mod;
for(int i=1;i<=n;i++) Hash[i]=(Hash[i-1]*p+s[i]-'a'+1)%mod;
for(int i=1;i<=n;i++) Insert(s[i]-'a',i);
top_sort(n);
} main() {
pre();
ll l=1,r=size[1]-1,mid;
while(l<r) {
mid=l+r>>1;
if(solve(mid)) r=mid;
else l=mid+1;
}
ed=0,len=0;
Find_kth(1,l);
rs=ed,ls=rs-len+1;
for(int i=ls;i<=rs;i++) cout<<s[i];
return 0;
for(int i=1;i<=size[1]-1;i++) {
ed=0,len=0;
Find_kth(1,i);
rs=ed,ls=ed-len+1;
for(int i=ls;i<=rs;i++) cout<<s[i];
cout<<"\n";
}
return 0;
}

【BZOJ4310】跳蚤的更多相关文章

  1. [BZOJ4310] 跳蚤 - 后缀数组,二分,ST表

    [BZOJ4310] 跳蚤 Description 首先,他会把串分成不超过 \(k\) 个子串,然后对于每个子串 \(S\) ,他会从 \(S\) 的所有子串中选择字典序最大的那一个,并在选出来的 ...

  2. BZOJ4310: 跳蚤 【后缀数组+二分】

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

  3. BZOJ4310 跳蚤(后缀数组+二分答案)

    注意到答案一定是原串的子串,于是考虑造出SA,二分答案是第几小的子串.第k小子串很容易在SA上求出.之后计算使他成为最大子串至少要在几个位置切割,对每个字典序比答案大的后缀,找到所有合法切割位置(求l ...

  4. BZOJ4310 : 跳蚤

    首先求出后缀数组,得到本质不同的子串的个数. 然后二分答案,每次先通过后缀数组求出第$mid$小的子串,然后贪心进行检验. 检验的时候,从后往前贪心,每次加入一个后缀,如果不能加了,那就划为一段. 时 ...

  5. [BZOJ4310] 跳蚤 SAM || SA

    没有代码的. 传送门 先二分出第 \(mid\) 大的字串 \(s\),然后从后往前切割,每次大于 \(s\) 了就不行. 涉及到的操作:求第 \(mid\) 大子串:比较两个字串(求 \(lcp\) ...

  6. 【算法】后缀自动机(SAM) 例题

    算法介绍见:http://www.cnblogs.com/Sakits/p/8232402.html 广义SAM资料:https://www.cnblogs.com/phile/p/4511571.h ...

  7. 【STACK】Several待填的坑

    待学的习: https://www.cnblogs.com/xiao-ju-ruo-xjr/p/9149792.html 待写的题: loj#3184:「CEOI2018」斐波那契表示法 luoguP ...

  8. 【BZOJ-4310】跳蚤 后缀数组 + ST表 + 二分

    4310: 跳蚤 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 180  Solved: 83[Submit][Status][Discuss] De ...

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

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

随机推荐

  1. Python 判断文件/目录是否存在

    使用 os 模块 判断文件是否存在 os.path.isfile(path) 判断目录是否存在 os.path.isdir(path) 判断路径是否存在 # 使用 path 模块 os.path.ex ...

  2. SpringBoot入门之集成Druid

    Druid:为监控而生的数据库连接池.这篇先了解下它的简单使用,下篇尝试用它做多数据源配置.主要参考:https://github.com/alibaba/druid/wiki/常见问题 https: ...

  3. Josephus Problem的详细算法及其Python、Java实现

      笔者昨天看电视,偶尔看到一集讲述古罗马人与犹太人的战争--马萨达战争,深为震撼,有兴趣的同学可以移步:http://finance.ifeng.com/a/20170627/15491157_0. ...

  4. SQL Server 中的 NOLOCK 到底是什么意思?

    以前遇到过,但仅限于听同事说加上NOLOCK好一些,今天仔细研究测试了下,终于理解了,那么加与不加到底区别在哪呢? 我先说下其区别,之后再做测试. 大家都知道,每新建一个查询,都相当于创建一个会话,在 ...

  5. 分部类,分部方法 - 修饰符partial

    一.分部类 什么是部分类呢?简单来说就是将一个类型或方法拆分到两个或多个源文件中,每个源文件只包含类型定义的一部分. 当使用自动生成的源时,无须重新创建源文件便可将代码添加到类中.Visual Stu ...

  6. 数据结构-Python3.7<三>

    上一篇:流程控制-Python<二> 因为列表具有pop.append.insert方法,因此列表可以当作堆.栈使用.由于性能问题,不建议当作堆.(堆:队列优先,先进先出(FIFO—fir ...

  7. 【Tomcat】压力测试和优化

    一.采用jmeter进行测试 为什么使用jmeter, 它免费开源, 不断发展, 功能逐渐强大. 可以做功能,负载, 性能测试.一套脚本可以同时用于功能和性能测试.Jmeter 有着众多的插件开发者, ...

  8. Django Rest Framework之版本控制

    基本代码结构 settings.py: REST_FRAMEWORK = { 'DEFAULT_VERSION': 'v1', # 默认版本 'ALLOWED_VERSIONS': ['v1', 'v ...

  9. CloudSim——云计算仿真软件概述

    CloudSim是由澳大利亚墨尔本大学的网格实验室和Gridbus项目宣布推出的云计算仿真软件. CloudSim是做什么的呢?可以简单理解为一个帮助研究.开发.测试的工具,如虚拟机资源分配算法.节能 ...

  10. MVC与单元测试实践之健身网站(四)-动作管理

    网站后台负责进行动作的管理,包括动作名称.介绍.训练要点.配图等内容,以便前台能够使用这些内容.在上一篇< Fit项目图片上传和云存储的调通>中已经准备好了这里涉及到的主要技术难点,现在就 ...