bzoj4310: 跳蚤
Description
很久很久以前,森林里住着一群跳蚤。一天,跳蚤国王得到了一个神秘的字符串,它想进行研究。首先,他会把串
分成不超过 k 个子串,然后对于每个子串 S,他会从S的所有子串中选择字典序最大的那一个,并在选出来的 k
个子串中选择字典序最大的那一个。他称其为“魔力串”。现在他想找一个最优的分法让“魔力串”字典序 最小(题目有误)。
Input
第一行一个整数 k,K<=15
接下来一个长度不超过 10^5 的字符串 S。
Output
输出一行,表示字典序最大的“魔力串”。
Sample Input
2
ababa
Sample Output
ba
//解释:
分成aba和ba两个串,其中字典序最大的子串为ba
分析
最大值最小肯定是二分答案
二分答案是第mid大的字符串
然后贪心
从尾扫到头一直加字符直到这个字符串比我们二分的答案更大
然后切一段
考虑到切的段越少说明我们的字符串越容易让某段的字符满足条件,那么我们就可以切更小的字符串。
那么剩下我们要做的就是兹瓷线性第k大和log的字符串比较
第k大我用的是后缀自动机,字符串比较我用的是Hash
因为是第一次写所以发一个算法流程:
字符串比较和找最长公共前缀是一个操作。
我们从尾到头秦九韶(用字符串的离散后字符做系数),然后一段子串的Hash值就是子串头的秦九韶的值减去子串尾的值承上随机数种子的n次方(相当于%吧)
二分答案即可。
听说这题用后缀数组有个很神的办法
然而我不知道~~
代码
比较烦,码农日常。
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>
using namespace std;
const int N = 4e5+50;
const int sand = 13331;
int fa[N], len[N], ch[N][26], s[N], str[N], c[N], q[N], last, sz, top, K;
long long ans, sum[N], val[N];
unsigned long long h[N], sh[N], bin[N];
void Sam_Extend(int c) {
int p = last, np = last = ++sz;
len[np] = len[p] + 1; val[np] = 1;
for(;p && !ch[p][c]; p = fa[p]) ch[p][c] = np;
if(!p) fa[np] = 1;
else {
int q = ch[p][c];
if(len[q] == len[p] + 1) fa[np] = q;
else {
int nq = ++sz; len[nq] = len[p] + 1;
memcpy(ch[nq], ch[q], sizeof(ch[q]));
fa[nq] = fa[q];
fa[q] = fa[np] = nq;
for(;ch[p][c] == q; p = fa[p]) ch[p][c] = nq;
}
}
}
void Sam_Build() {
char ch = getchar(); int x = 0; last = ++sz;
while(ch < 'a' || ch > 'z') ch = getchar();
for(;ch >= 'a' && ch <= 'z'; ch = getchar()) Sam_Extend(s[++x] = ch - 'a');
s[0] = x;
for(int i = s[0]; i; --i) h[i] = h[i + 1] * sand + s[i];
bin[0] = 1; for(int i = 1;i <= s[0]; ++i) bin[i] = bin[i - 1] * sand;
}
void Sam_Pre() {
for(int i = 1;i <= sz; ++i) ++c[len[i]];
for(int i = 1;i <= s[0]; ++i) c[i] += c[i - 1];
for(int i = sz; i; --i) q[c[len[i]]--] = i;
for(int i = sz; i; --i) val[i] = 1;
val[1] = 0;
for(int i = sz; i; --i) {
int t = q[i]; sum[t] = val[t];
for(int c = 0;c < 26; ++c)
sum[t] += sum[ch[t][c]];
}
}
void Sam_Kth(int u, long long k) {
if(k <= val[u]) return;
k -= val[u]; int v;
for(int i = 0;i < 26; ++i)
if(v = ch[u][i]) {
if(k <= sum[v]) {
str[++top] = i;
Sam_Kth(v, k);
return ;
}
k -= sum[v];
}
}
unsigned long long hashs(int L, int R) {return h[L] - h[R + 1] * bin[R - L + 1];}
unsigned long long hashc(int L, int R) {return sh[L] - sh[R + 1] * bin[R - L + 1];}
bool cmp(int L, int R) {
if(str[1] < s[L]) return false;
if(str[1] > s[L]) return true;
int l = 1, r = min(top, R - L + 1), ans;
while(l <= r) {
int mid = l + r >> 1;
if(hashc(1, mid) == hashs(L, L + mid - 1)) {
ans = mid;
l = mid + 1;
}
else r = mid - 1;
} ++ans;
if(ans > R - L + 1) return true;
if(ans > top) return false;
if(str[ans] < s[L + ans - 1]) return false;
return true;
}
bool check() {
int ret = 0;
for(int i = s[0], p; i; i = p) {
p = i;
while(p && cmp(p, i)) --p;
if(p == i) return false;
++ret;
}
return ret <= K;
}
void Binary() {
long long L = 1, R = sum[1];
while(L <= R) {
long long mid = L + R >> 1;
top = 0; Sam_Kth(1, mid); sh[top + 1] = 0;
for(int i = top; i; --i) sh[i] = sh[i + 1] * sand + str[i];
if(check()) {
ans = mid;
R = mid - 1;
}
else L = mid + 1;
}
}
int main() {
scanf("%d", &K);
Sam_Build();
Sam_Pre();
Binary();
top = 0; Sam_Kth(1, ans);
for(int i = 1;i <= top; ++i) putchar(str[i] + 'a');
putchar('\n');
return 0;
}