bzoj 4310 跳蚤 二分答案+后缀数组/后缀树
题目大意
给定\(k\)和长度\(\le10^5\)的串S
把串分成不超过\(k\)个子串,然后对于每个子串\(s\),他会从\(s\)的所有子串中选择字典序最大的那一个,并在选出来的\(k\)个子串中再选择字典序最大的那一个。他称其为“魔力串”。
输出最小的魔力串
分析
最大值最小\(\Rightarrow\)二分+判定性问题
考虑对于选出来的\(k\)个子串\(s\),\(s\)中最大子串一定是\(s\)的某个后缀
做法
我们在所有本质不同字符串中按找字典序进行二分
得到一段字符
因为\(s\)中最大子串一定是\(s\)的某个后缀
我们从后往前扫(从前往后就\(n^2\)了),不行就分多一段
记录last表示上一次分割的地方
那么扫到\(i\)时\(i-last\)就是当前\(s\)的后缀
比较一下即可\(~~\) cmp调了一个小时还好意思说即可
bool cmp(int x,int y,int l1,int l2){//s[x..x+l1-1],s[y..y+l2-1]
int tp=lcp(x,y);
if(tp<l1&&tp<l2) return s[x+tp]>s[y+tp];//在比较范围直接比较
return l1>l2; //否则直接比较长度
}
实现用后缀数组方便许多
后缀树麻烦一点
solution
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef long long LL;
const int M=200007;
int n,m,st,len;
char s[M];
int id[M];
int last,tot;
int ch[M][26];
int fa[M],stp[M];
int ed[M];
int dfn[M],pid[M],tdfn;
int pre[M][20],dep[M],Mx;
LL sum[M];
struct edge{int y,nxt;};
struct vec{
int g[M],te;
edge e[M];
vec(){memset(g,0,sizeof(g)); te=0;}
void clear(){memset(g,0,sizeof(g)); te=0;}
inline void push(int x,int y){e[++te].y=y;e[te].nxt=g[x];g[x]=te;}
inline int& operator () (int &x) {return g[x];}
inline edge& operator [] (int &x) {return e[x];}
}go,chr;
int newnode(int ss){
stp[++tot]=ss;
return tot;
}
int ext(int p,int q,int d){
int nq=newnode(stp[p]+1); ed[nq]=ed[q]-(stp[q]-(stp[p]+1));
fa[nq]=fa[q]; fa[q]=nq;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
for(;p&&ch[p][d]==q;p=fa[p]) ch[p][d]=nq;
return nq;
}
int sam(int p,int d){
int np=ch[p][d];
if(np) return (stp[p]+1==stp[np]) ? np : ext(p,np,d);
np=newnode(stp[p]+1); ed[np]=n;
for(;p&&!ch[p][d];p=fa[p]) ch[p][d]=np;
if(!p) fa[np]=1;
else{
int q=ch[p][d];
fa[np]= (stp[p]+1==stp[q]) ? q : ext(p,q,d);
}
return np;
}
void dfs(int x){
dfn[x]=++tdfn;
pid[tdfn]=x;
sum[tdfn]=stp[x]-stp[fa[x]];
int p,y;
for(p=go(x);p;p=go[p].nxt){
y=go[p].y;
dep[y]=dep[x]+1;
pre[y][0]=x;
dfs(y);
}
}
int LCA(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
for(int t=Mx;t>=0;t--)
if(dep[pre[x][t]]>=dep[y]) x=pre[x][t];
if(x==y) return x;
for(int t=Mx;t>=0;t--)
if(pre[x][t]!=pre[y][t]) x=pre[x][t],y=pre[y][t];
return pre[x][0];
}
int find(LL num){
int l=1,r=tdfn,mid;
while(l<r){
mid=l+r>>1;
if(sum[mid]>=num) r=mid;
else l=mid+1;
}
return l;
}
void getkth(LL num){
int ps=find(num);
int p=pid[ps];
num=sum[ps]-num;
st=ed[p]-stp[p]+1;
len=stp[p]-num;
}
int lcp(int x,int y){
return stp[LCA(id[x],id[y])];
}
bool cmp(int x,int y,int l1,int l2){
int tp=lcp(x,y);
if(tp<l1&&tp<l2) return s[x+tp]>s[y+tp];
return l1>l2;
}
bool check(){
int i,lst=n,blk=0;
for(i=n;i>0;i--){
if(s[i]>s[st]) return 0;
if(cmp(i,st,lst-i+1,len)) blk++,lst=i;
}
return blk+1<=m;
}
int main(){
int i,j,p;
scanf("%d",&m);
scanf("%s",s+1);
n=strlen(s+1);
last=tot=1;
for(i=n;i>0;i--) id[i]=last=sam(last,s[i]-'a');
for(i=2;i<=tot;i++)
chr.push(s[ed[i]-(stp[i]-stp[fa[i]])+1]-'a',i);
for(i=26;i>=0;i--)
for(p=chr(i);p;p=chr[p].nxt)
go.push(fa[chr[p].y],chr[p].y);
dfs(1);
Mx=log2(tot);
for(j=1;j<=Mx;j++)
for(i=1;i<=tot;i++) pre[i][j]=pre[pre[i][j-1]][j-1];
for(i=1;i<=tdfn;i++) sum[i]+=sum[i-1];
LL l=1,r=sum[tdfn],mid;
while(l<r){
mid=l+(r-l)/2;
getkth(mid);
if(check()) r=mid;
else l=mid+1;
}
getkth(l);
for(i=st;i<=st+len-1;i++) printf("%c",s[i]); puts("");
return 0;
}
bzoj 4310 跳蚤 二分答案+后缀数组/后缀树的更多相关文章
- BZOJ 2653 middle 二分答案+可持久化线段树
题目大意:有一个序列,包含多次询问.询问区间左右端点在规定区间里移动所得到的最大中位数的值. 考虑对于每个询问,如何得到最优区间?枚举显然是超时的,只能考虑二分. 中位数的定义是在一个序列中,比中位数 ...
- 【整理】如何选取后缀数组&&后缀自动机
后缀家族已知成员 后缀树 后缀数组 后缀自动机 后缀仙人掌 后缀预言 后缀Splay ? 后缀树是后缀数 ...
- 字符串的模板 Manacher kmp ac自动机 后缀数组 后缀自动机
为何scanf("%s", str)不需要&运算 经常忘掉的字符串知识点,最好不加&,不加&最标准,指针如果像scanf里一样加&是错的,大概是未定 ...
- loj6173 Samjia和矩阵(后缀数组/后缀自动机)
题目: https://loj.ac/problem/6173 分析: 考虑枚举宽度w,然后把宽度压位集中,将它们哈希 (这是w=2的时候) 然后可以写一下string=“ac#bc” 然后就是求这个 ...
- bzoj 4310 跳蚤 —— 后缀数组+二分答案+贪心
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4310 二分答案——在本质不同的子串中二分答案! 如果二分到的子串位置是 st,考虑何时必须分 ...
- 后缀数组 hash求LCP BZOJ 4310: 跳蚤
后缀数组的题博客里没放进去过..所以挖了一题写写 充实下博客 顺便留作板子.. 一个字符串S中 内容不同的子串 有 sigma{n-sa[i]+1-h[i]} (噢 这里的h[]就是大家熟知的he ...
- BZOJ3277 串 【后缀数组】【二分答案】【主席树】
题目分析: 用"$"连接后缀数组,然后做一个主席树求区间内不同的数的个数.二分一个前缀长度再在主席树上求不同的数的个数. 代码: #include<bits/stdc++.h ...
- BZOJ 2946 [Poi2000]公共串 (二分+Hash/二分+后缀数组/后缀自动机)
求多串的最长公共字串. 法1: 二分长度+hash 传送门 法2: 二分+后缀数组 传送门 法3: 后缀自动机 拿第一个串建自动机,然后用其他串在上面匹配.每次求出SAM上每个节点的最长匹配长度后,再 ...
- BZOJ 4556(后缀数组+主席树求前驱后继+二分||后缀数组+二分+可持久化线段树)
换markdown写了.. 题意: 给你一个1e5的字符串,1e5组询问,求\([l_1,r_1]\)的所有子串与\([l_2,r_2]\)的lcp 思路: 首先可以发现答案是具有单调性的,我们考虑二 ...
随机推荐
- c#基础之循环探索
前言在学习基础的语法中循环控制是程序语句控制中的一种,循环在很多的操作中都有应用,例如在获得数据库中的查询的数据之后可以用循环遍历的方式拿到每一行的数据,从而拿到每一个单元格的数据,在文件的操作中也大 ...
- testC-I
总时间限制: 20000ms 单个测试点时间限制: 1000ms 内存限制: 128000kB 描述 给你一组数,a1,a2,a3,⋯,an. 令:G=gcd(a1,a2,a3,⋯,an) 现在 ...
- win7旗舰版64位java的jdk环境变量的配置(2012-12-26-bd 写的日志迁移
首先到oracle的官方网站http://www.oracle.com/technetwork/cn/java/javase/downloads/index.html下个JDK比如下图: 必须是win ...
- JZOJ 3508. 【NOIP2013模拟11.5B组】好元素
3508. [NOIP2013模拟11.5B组]好元素(good) (File IO): input:good.in output:good.out Time Limits: 2000 ms Mem ...
- 18.VUE学习之-v-for操作对象与数值
一组数组时的循环 二组数组时的循环 另外可以v for 20 可以直接操作数字 <!DOCTYPE html> <html lang="en"> <h ...
- ActiveMQ发布-订阅消息模式(同点对点模式的区别)
点对点与发布订阅最初是由JMS定义的.这两种模式主要区别或解决的问题就是发送到队列的消息能否重复消费(多订阅) 点对点: 消息生产者生产消息发送到queue中,然后消息消费者从queue中取出并且消费 ...
- linux的发展过程
1. 操作系统 人与计算机硬件直接的中介 2. Linux系统组成 3. Linux的发展过程 蛋-人-人-人 unix于诞生贝尔实验室 人-谭教授 谭宁邦 minix mini unix. 主要用于 ...
- 【转】iPhone通讯录AddressBook.framework和AddressBookUI.framework的应用
通讯录中联系人相关的应用iPhone提供了两个框架:AddressBook.framework和AddressBookUI.framework,使用这两个框架我们可以在程序中访问并显示iPhone数据 ...
- ubuntu怎样打开终端
1.首先在桌面任意空白处,按CTRL+ALT+T 2.方法二
- Linear Regression 线性回归
Motivation 问题描述 收集到某一地区的房子面积和房价的数据(x, y)42组,对于一套已知面积的房子预测其房价?   由房价数据可视化图可以看出,可以使用一条直线拟合房价.通过这种假设得 ...