不知道后缀数组的请退回去!

题面:

题目描述

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

输入格式

第一行一个整数 k,k≤15

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

输出格式

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

样例

输入样例

2
ababa

输出样例

ba

样例解释

分成aba和ba两个串,其中字典序最大的子串为ba

看到让最大的最小我们就想到二分答案,二分答案在原字符串的所有不同子串中的排名。知道了排名,我们用后缀数组就很好求出答案串是什么(记录其在原串中的起始位置和结束位置),具体方法见代码。

这里还有一点要考虑的是二分的上界也就是子串的个数。其实这很好求就是∑n-sa[i]+1-height[i[。毕竟所有的子串都是一个后缀的前缀,对于一个后缀sa[i],他有n-sa[i]+1个前缀,但是有height[i]个前缀与前面的重复,已经算过了,就得减掉。

然后我们来考虑如何判定。这里我默认大家都会求LCP(LCP(i, j)=min{height[k]}(rank[i]<k<=rank[j]),然后用ST表nlogn预处理,O(1)时间内求出LCP)。记录一个cut=i代表你上次在i-1和i之间切了一刀,令cut的初值为n+1。再记录一个cnt代表切了多少次,如果cnt>=k则不成立(这里注意切了cnt到右cnt+1个块,所以是>=)。每次判定先求出当且串的起始和结束位置记为L, R,然后再从后往前枚举后缀i,求出i和L的LCP。若LCP==0,则判断s[L]和s[i]的大小关系,若s[i]>s[L]则返回false(根据题目要求s[L…R]应是一个快内最大的)。求min{LCP, cut - i, R - L + 1}。若cut-i最小,则说明上次剪的地方到现在这一段都是相同的(<LCP)或者比当前串还短(<R-L+1),此时这个位置一定不需要剪,直接continue。若R-L+1最小或者LCP最小且s[L+LCP]<s[i+LCP]时我们就需要分块。令cut = i + 1,cnt++,然后再判断cnt与k的关系即可。

上代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = ;
ll k;
ll n, m;
ll sa[N], rnk[N], v1[N], v2[N], sum[N], height[N];
ll st[N][];
char s[N];
bool cmp(ll *t, ll a, ll b, ll l) {
return t[a] == t[b] && t[a + l] == t[b + l];
}
void da() {
ll i, j, p = ;
for (i = ; i <= m; i++) sum[i] = ;
for (i = ; i <= n; i++) sum[rnk[i] = s[i]]++;
for (i = ; i <= m; i++) sum[i] += sum[i - ];
for (i = n; i >= ; i--) sa[sum[rnk[i]]--] = i;
for (j = ; j <= n; j *= , m = p) {
for (p = , i = n - j + ; i <= n; i++) v2[++p] = i;
for (i = ; i <= n; i++) if (sa[i] > j) v2[++p] = sa[i] - j;
for (i = ; i <= n; i++) v1[i] = rnk[v2[i]];
for (i = ; i <= m; i++) sum[i] = ;
for (i = ; i <= n; i++) sum[v1[i]]++;
for (i = ; i <= m; i++) sum[i] += sum[i - ];
for (i = n; i >= ; i--) sa[sum[v1[i]]--] = v2[i];
for (swap(rnk, v2), rnk[sa[]] = , p = , i = ; i <= n; i++) {
rnk[sa[i]] = cmp(v2, sa[i - ], sa[i], j) ? p - : p++;
}
}
}
void calheight() {
ll i, j, p = ;
for (i = ; i <= n; i++) {
if (p) p--;
j = sa[rnk[i] - ];
while (s[i + p] == s[j + p]) p++;
height[rnk[i]] = p;
}
}
void st_pre() {
for (ll i = ; i <= n; i++) st[i][] = height[i];
for (ll j = ; j <= ; j++) {
for (ll i = ; i <= n; i++) {
if (i + ( << (j - )) > n) break;
st[i][j] = min(st[i][j - ], st[i + ( << (j - ))][j - ]);
}
}
}
ll LCP(ll l, ll r) {
if (l == r) return n - sa[l] + ;
if (l > r) swap(l, r);
l++;
ll kk = log(r - l + ) / log();
return min(st[l][kk], st[r - ( << kk) + ][kk]);
}
ll pos_l, pos_r, ans_l, ans_r;
void get_string(ll mid) {
for (ll i = ; i <= n; i++) {
ll tmp = n - sa[i] - height[i] + ;
if (mid > tmp) {
mid -= tmp;
} else {
pos_l = sa[i];
pos_r = sa[i] + height[i] - + mid;
return;
}
}
}
bool check() {
for (ll i = n, cut = n + , cnt = ; i >= ; i--) {
ll lcp = LCP(rnk[pos_l], rnk[i]);
if (lcp == && s[i] > s[pos_l]) return false;
lcp = min(lcp, min(pos_r - pos_l + , cut - i));
if (lcp == cut - i) continue;
if (lcp == pos_r - pos_l + || s[i + lcp] > s[pos_l + lcp]) {
cnt++;
cut = i + ;
if (cnt > k) return false;
}
} return true;
}
int main() {
scanf("%lld%s", &k, s + );
k--;
n = strlen(s + );
m = ;
da();
calheight();
st_pre();
ll l = , r = ;
for (ll i = ; i <= n; i++) {
r += n - sa[i] - height[i] + ;
}
while (l <= r) {
ll mid = (l + r) >> ;
get_string(mid);
if (check()) {
ans_l = pos_l;
ans_r = pos_r;
r = mid - ;
} else {
l = mid + ;
}
}
for (ll i = ans_l; i <= ans_r; i++) {
cout << s[i];
}
return ;
}

跳蚤[BZOJ4310](后缀数组+二分答案传判定)的更多相关文章

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

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

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

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

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

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

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

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

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

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

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

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

  7. POJ3294--Life Forms 后缀数组+二分答案 大于k个字符串的最长公共子串

                                                                              Life Forms Time Limit: 500 ...

  8. SPOJ 220 Relevant Phrases of Annihilation(后缀数组+二分答案)

    [题目链接] http://www.spoj.pl/problems/PHRASES/ [题目大意] 求在每个字符串中出现至少两次的最长的子串 [题解] 注意到这么几个关键点:最长,至少两次,每个字符 ...

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

    [题目链接] http://poj.org/problem?id=3261 [题目大意] 求最长可允许重叠的出现次数不小于k的子串. [题解] 对原串做一遍后缀数组,二分子串长度x,将前缀相同长度超过 ...

随机推荐

  1. 三分钟让你秒懂.Net生态系统

    提到.Net的时候,大多数人的第一反应可能就是.Net Framework和Visual Studio..Net Framework的第一个版本发布与2002年2月13日,这对于科技发展日新月异的时代 ...

  2. 吴裕雄--天生自然Numpy库学习笔记:NumPy IO

    Numpy 可以读写磁盘上的文本数据或二进制数据. NumPy 为 ndarray 对象引入了一个简单的文件格式:npy. npy 文件用于存储重建 ndarray 所需的数据.图形.dtype 和其 ...

  3. 关于转入软件工程专业后第二次java课上作业的某些体会

    今天是第二周的java课. 自从转入了软件工程专业后,在我没有学习c++的基础上,直接开始了学习java的过程.不得不说过程很艰辛.今天下午老师让编写一个随机产生作业的软件.而我的基础差到都不知道如何 ...

  4. Visual Studio 2017安装MSDN

      在学习Visual Studio 2017的过程中,总会遇到各种各样的难题,这时候你就会求助书或者是网上大佬们的解释,但是在看视频的过程中,我发现了MSDN这个“好东西”,就立马应用于实践,下面把 ...

  5. 如何删除 AppStore 中的恶意评论 iOS

    AppStore 中的评论,对于产品的形象影响很大.如果评论榜中出现了恶意评论,会对产品形象影响很大,当然这些差评有可能是用户的真实反馈,需要产品设计人员做好产品设计,满足客户的需求.另外也可能是竞争 ...

  6. mysqld: Can't change dir to 'D:\TONG\mysql-5.7.19-winx64\data\' (Errcode: 2 - No such file or directory)

    mysqld: Can't change dir to 'D:\TONG\mysql-5.7.19-winx64\data\' (Errcode: 2 - No such file or direct ...

  7. python列表操作方法详解

      列表 列表是Python中最基本的数据结构,列表是最常用的Python数据类型,列表是一个数据的集合,集合内可以放任何数据类型,可对集合方便的增删改查操作.Python已经内置确定序列的长度以及确 ...

  8. RestTemplate HttpMessageConverter报错的解决方案no suitable HttpMessageConverter

    错误 no suitable HttpMessageConverter found for response type and content type [text/html;charset=UTF- ...

  9. JDBC连接MySql例子

    1.注册MySql连接驱动 2.设置连接MySql连接字符串.用户名和密码 3.获取数据库连接 代码如下: // 加载驱动 Class.forName("com.mysql.jdbc.Dri ...

  10. 如何安装部署和优化Tomcat?(Tomcat部署和优化与压测,虚拟主机配置,Tomcat处理请求的过程)

    文章目录 前言 一:Tomcat安装部署 1.1:Tomcat简介 1.2:Tomcat核心组件 1.3:Tomcat处理请求的过程 1.3.1:请求过程基本解释 1.3.2:请求过程详细解释 1.4 ...