题目描述

很久很久以前,森林里住着一群跳蚤。一天,跳蚤国王得到了一个神秘的字符串,它想进行研究。
首先,他会把串分成不超过 k 个子串,然后对于每个子串 S,他会从S的所有子串中选择字典序最大的那一个,并在选出来的 k 个子串中选择字典序最大的那一个。他称其为“魔力串”。
现在他想找一个最优的分法让“魔力串”字典序最小。

输入

第一行一个整数 k。
接下来一个长度不超过 105 的字符串 S。

输出

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

样例输入

13
bcbcbacbbbbbabbacbcbacbbababaabbbaabacacbbbccaccbcaabcacbacbcabaacbccbbcbcbacccbcccbbcaacabacaaaaaba

样例输出

cbc


题解

后缀数组+二分

先使用后缀数组求出sa、rank和height,然后预处理出ST表,用倍增RMQ求LCP(再次偷改height的定义,height[i][j]表示sa[i]与sa[i-(1<<j)]的LCP)

然后我们二分答案串在S的所有子串中的排名。

这里用到了一个挺好理解的结论:一个串的本质不同的子串的个数为$\sum\limits_{i=1}^nn-sa[i]-height[i]$,就是某个字符开头的串的个数-重复出现过的串的个数。

有个这个结论可以做点什么?首先我们可以确定二分边界。

然后我们还可以根据子串的排名mid来求出对应的子串。

怎么求?我们从前往后枚举i,算出以sa[i]开头的本质不同的子串个数n-sa[i]-height[i],如果mid大于这个数就将mid减去这个数,否则对应的子串就是sa[i]开头,sa[i]+height[i]-1+mid结尾的字符串。退推一下,应该不是很难想。

那么有了子串以后,我们在原串上从后往前贪心,每次找到一个字符,就比较当前串和二分的子串的字典序大小关系,如果当前的子串字典序较大,则在这个找到的字符后面分割一下。最后比较分割次数与k的关系即可。

需要注意的是二分的子串必须大于等于单个的最大字符,否则无论怎样分割都不可能分割出小于等于该串的字符串。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100010
using namespace std;
typedef long long ll;
int sa[N] , r[N] , ws[N] , wa[N] , wb[N] , rank[N] , height[N][20] , n , m = 27 , log[N] , k , L , R;
char str[N];
void getsa()
{
int i , j , p , *x = wa , *y = wb;
for(i = 0 ; i < n ; i ++ ) ws[x[i] = r[i]] ++ ;
for(i = 1 ; i < m ; i ++ ) ws[i] += ws[i - 1];
for(i = n - 1 ; i >= 0 ; i -- ) sa[--ws[x[i]]] = i;
for(p = j = 1 ; p < n ; j <<= 1 , m = p)
{
for(p = 0 , i = n - j ; i < n ; i ++ ) y[p ++ ] = i;
for(i = 0 ; i < n ; i ++ ) if(sa[i] - j >= 0) y[p ++ ] = sa[i] - j;
for(i = 0 ; i < m ; i ++ ) ws[i] = 0;
for(i = 0 ; i < n ; i ++ ) ws[x[y[i]]] ++ ;
for(i = 1 ; i < m ; i ++ ) ws[i] += ws[i - 1];
for(i = n - 1 ; i >= 0 ; i -- ) sa[--ws[x[y[i]]]] = y[i];
for(swap(x , y) , x[sa[0]] = 0 , p = i = 1 ; i < n ; i ++ )
{
if(y[sa[i]] == y[sa[i - 1]] && y[sa[i] + j] == y[sa[i - 1] + j]) x[sa[i]] = p - 1;
else x[sa[i]] = p ++ ;
}
}
for(i = 1 ; i < n ; i ++ ) rank[sa[i]] = i;
for(p = i = 0 ; i < n - 1 ; height[rank[i ++ ]][0] = p)
for(p ? p -- : 0 , j = sa[rank[i] - 1] ; r[i + p] == r[j + p] ; p ++ );
}
void query(ll mid)
{
int i;
for(i = 1 ; i <= n ; i ++ )
{
if(n - sa[i] - height[i][0] < mid) mid -= n - sa[i] - height[i][0];
else
{
L = sa[i] , R = sa[i] + height[i][0] + mid - 1;
return;
}
}
L = 0 , R = n - 1;
}
int lcp(int x , int y)
{
if(x == y) return n;
x = rank[x] , y = rank[y];
if(x > y) swap(x , y);
int k = log[y - x];
return min(height[y][k] , height[x + (1 << k)][k]);
}
bool cmp(int l1 , int r1 , int l2 , int r2)
{
int t = lcp(l1 , l2) , len1 = r1 - l1 + 1 , len2 = r2 - l2 + 1;
if(len1 <= len2 && len1 <= t) return 1;
if(len1 > len2 && len2 <= t) return 0;
if(len1 <= t && len2 <= t) return len1 <= len2;
return r[l1 + t] <= r[l2 + t];
}
bool judge()
{
int i , cnt = 1 , last = n - 1;
for(i = n - 1 ; ~i ; i -- )
{
if(r[i] > r[L]) return 0;
if(!cmp(i , last , L , R)) cnt ++ , last = i;
if(cnt > k) return 0;
}
return 1;
}
int main()
{
int i , j;
ll lp = 1 , rp = 0 , mid , ans;
scanf("%d%s" , &k , str) , n = strlen(str);
for(i = 0 ; i < n ; i ++ ) r[i] = str[i] - 'a' + 1;
n ++ , getsa() , n -- ;
for(i = 2 ; i <= n ; i ++ ) log[i] = log[i >> 1] + 1;
for(i = 1 ; (1 << i) <= n ; i ++ )
for(j = (1 << i) ; j <= n ; j ++ )
height[j][i] = min(height[j][i - 1] , height[j - (1 << (i - 1))][i - 1]);
for(i = 1 ; i <= n ; i ++ ) rp += n - sa[i] - height[i][0];
while(lp <= rp)
{
mid = (lp + rp) >> 1 , query(mid);
if(judge()) ans = mid , rp = mid - 1;
else lp = mid + 1;
}
query(ans);
for(i = L ; i <= R ; i ++ ) putchar(str[i]);
return 0;
}

【bzoj4310】跳蚤 后缀数组+二分的更多相关文章

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

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

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

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

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

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

  4. BZOJ 3230: 相似子串( RMQ + 后缀数组 + 二分 )

    二分查找求出k大串, 然后正反做后缀数组, RMQ求LCP, 时间复杂度O(NlogN+logN) -------------------------------------------------- ...

  5. BZOJ_2946_[Poi2000]公共串_后缀数组+二分答案

    BZOJ_2946_[Poi2000]公共串_后缀数组+二分答案 Description          给出几个由小写字母构成的单词,求它们最长的公共子串的长度. 任务: l        读入单 ...

  6. BZOJ 1717 [USACO06DEC] Milk Patterns (后缀数组+二分)

    题目大意:求可重叠的相同子串数量至少是K的子串最长长度 洛谷传送门 依然是后缀数组+二分,先用后缀数组处理出height 每次二分出一个长度x,然后去验证,在排序的后缀串集合里,有没有连续数量多于K个 ...

  7. POJ 1743 [USACO5.1] Musical Theme (后缀数组+二分)

    洛谷P2743传送门 题目大意:给你一个序列,求其中最长的一对相似等长子串 一对合法的相似子串被定义为: 1.任意一个子串长度都大于等于5 2.不能有重叠部分 3.其中一个子串可以在全部+/-某个值后 ...

  8. Poj 1743 Musical Theme(后缀数组+二分答案)

    Musical Theme Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 28435 Accepted: 9604 Descri ...

  9. Poj 3261 Milk Patterns(后缀数组+二分答案)

    Milk Patterns Case Time Limit: 2000MS Description Farmer John has noticed that the quality of milk g ...

随机推荐

  1. JavaScript模板引擎的使用

    为了将数据库中的一组记录转换成HTML输出到界面上,大家都采用哪些做法呢? 在WebForm时代我们经常使用datagrid.repeater,当MVC问世后我们开始直接在视图上编写C#循环语句,而现 ...

  2. 关于火狐浏览器在ubuntu和安卓手机上的同步

    最近在ubuntu使用火狐浏览器,感觉还不错.我想着,如果在我的安卓手机上装一个火狐浏览器,我就可以在手机上查看电脑上所收藏的网站了.然后我就去安卓应用市场下载了最新版的火狐浏览器.令人奇怪的是,我在 ...

  3. HDU 5451 Best Solver(fibonacci)

    感谢这道题让我复习了一遍线代,还学习了一些奇奇怪怪的数论. 令 二项展开以后根号部分抵消了 显然有 所以要求的答案是 如果n比较小的话,可以直接对二项式快速幂,但是这题n很大 这个问题和矩阵的特征值以 ...

  4. codeforce Gym 100500A Poetry Challenge(博弈,暴搜)

    题解:状态压缩之后,暴力dfs,如果有一个选择,能让对手必败,那么就是必胜态,能转移到的状态都是对手的必胜态,或者无法转移,就是必败态. 总算是过了,TLE是因为状态没判重. #include< ...

  5. Python-OpenCV——Image inverting

    通常我们将读入的彩色图转化成灰度图,需要将灰度图反转得到掩码,如何正确快速的得到某个图像的反转图呢? 首先看一种看似很正确的写法,对其中每个像素进行如下处理: img[x,y] = abs(img[x ...

  6. Happy Equation

    Source: The 10th Shandong Provincial Collegiate Programming Contest 题解: 因为2^p为偶数,所以a,x的奇偶性相同 1.当a为奇数 ...

  7. 用 label 控制 Pod 的位置

    默认配置下,Scheduler 会将 Pod 调度到所有可用的 Node.不过有些情况我们希望将 Pod 部署到指定的 Node,比如将有大量磁盘 I/O 的 Pod 部署到配置了 SSD 的 Nod ...

  8. 01_3_创建一个Servlet

    01_3_创建一个Servlet 1.创建一个Servlet import java.io.IOException; import java.io.PrintWriter; import javax. ...

  9. MySQL 使用GTID进行复制

    MySQL 使用GTID进行复制 1. GTID的格式和存储 1.1 GTID 集 1.2 mysql.gtid_executed 表 1.3 mysql.gtid_executed 表压缩 2. G ...

  10. Java-JFrame-swing嵌套浏览器步骤

    Java-JFrame-swing嵌套浏览器步骤 一.使用swing嵌套浏览器要实现的功能: 通过java的swing实现在一个窗体中嵌套一个浏览器,可以在这个浏览器中将另一个项目的内容显示出来,只需 ...