题目大意

给定\(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 跳蚤 二分答案+后缀数组/后缀树的更多相关文章

  1. BZOJ 2653 middle 二分答案+可持久化线段树

    题目大意:有一个序列,包含多次询问.询问区间左右端点在规定区间里移动所得到的最大中位数的值. 考虑对于每个询问,如何得到最优区间?枚举显然是超时的,只能考虑二分. 中位数的定义是在一个序列中,比中位数 ...

  2. 【整理】如何选取后缀数组&&后缀自动机

    后缀家族已知成员         后缀树         后缀数组         后缀自动机         后缀仙人掌         后缀预言         后缀Splay ? 后缀树是后缀数 ...

  3. 字符串的模板 Manacher kmp ac自动机 后缀数组 后缀自动机

    为何scanf("%s", str)不需要&运算 经常忘掉的字符串知识点,最好不加&,不加&最标准,指针如果像scanf里一样加&是错的,大概是未定 ...

  4. loj6173 Samjia和矩阵(后缀数组/后缀自动机)

    题目: https://loj.ac/problem/6173 分析: 考虑枚举宽度w,然后把宽度压位集中,将它们哈希 (这是w=2的时候) 然后可以写一下string=“ac#bc” 然后就是求这个 ...

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

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

  6. 后缀数组 hash求LCP BZOJ 4310: 跳蚤

    后缀数组的题博客里没放进去过..所以挖了一题写写 充实下博客 顺便留作板子.. 一个字符串S中 内容不同的子串 有 sigma{n-sa[i]+1-h[i]}   (噢 这里的h[]就是大家熟知的he ...

  7. BZOJ3277 串 【后缀数组】【二分答案】【主席树】

    题目分析: 用"$"连接后缀数组,然后做一个主席树求区间内不同的数的个数.二分一个前缀长度再在主席树上求不同的数的个数. 代码: #include<bits/stdc++.h ...

  8. BZOJ 2946 [Poi2000]公共串 (二分+Hash/二分+后缀数组/后缀自动机)

    求多串的最长公共字串. 法1: 二分长度+hash 传送门 法2: 二分+后缀数组 传送门 法3: 后缀自动机 拿第一个串建自动机,然后用其他串在上面匹配.每次求出SAM上每个节点的最长匹配长度后,再 ...

  9. BZOJ 4556(后缀数组+主席树求前驱后继+二分||后缀数组+二分+可持久化线段树)

    换markdown写了.. 题意: 给你一个1e5的字符串,1e5组询问,求\([l_1,r_1]\)的所有子串与\([l_2,r_2]\)的lcp 思路: 首先可以发现答案是具有单调性的,我们考虑二 ...

随机推荐

  1. 20180909 解析JS Cookie的设置,获取和检索

    引用: JavaScript Cookie - by runoob.com Cookie是储存在电脑文本文件中的数据,用于保存访问者的信息,并可以在下次打开页面时引用. 页面在设置/引用访问者信息时, ...

  2. 洛谷P1111修复公路并查集改

    看了他们的题解感觉很震惊,为什么要用kruskal,这题要用到最小生成树吗??? 38行短短的程序就可以了,我觉得学习不是一种套用,套自己学的,而且题解很大一部分都是kruskal. 个人认为自己的程 ...

  3. 【NTT】bzoj3992: [SDOI2015]序列统计

    板子题都差点不会了 Description 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数 列,数列中的每个数都属于集合S.小C用这个生成器生 ...

  4. 原生js替换jQuery各种方法-中文版

    原文https://github.com/nefe/You-D... 原生JS与jQuery操作DOM对比 You Don't Need jQuery 前端发展很快,现代浏览器原生 API 已经足够好 ...

  5. 并查集:HDU4496-D-City(倒用并查集)

    D-City Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65535/65535 K (Java/Others) Total Submis ...

  6. 处理IE6下PNG图片透明背景问题

    由于历史原因,IE较早的版本不支持PNG透明 可以支持GIF等的透明 由于png图片相对较小,所以很多网站还是青睐于PNG图片 最近就遇到这种情况,使用js和css滤镜来实现的与大家分享一下下: 首先 ...

  7. HDU1042 A * B Problem Plus

    A * B Problem Plus Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Other ...

  8. angular用$sce服务来过滤HTML标签

    angular js的强大之处之一就是他的数据双向绑定这一牛B功能,我们会常常用到的两个东西就是ng-bind和针对form的ng-model.但在我们的项目当中会遇到这样的情况,后台返回的数据中带有 ...

  9. luoguT30204 偷上网

    \(n=1\) 时特判四角,其余时刻圆的面积和必小于正方形面积,随机点出来判断就行了. stm 随机算法-- #include <iostream> #include <cstdli ...

  10. cf979d Kuro and GCD and XOR and SUM

    set做法 正解是trie-- 主要是要学会 \(a\ \mathrm{xor}\ b \leq a+b\) 这种操作 #include <iostream> #include <c ...