题目描述
给定一个长度为 的环状字符串。
将这个字符串划分成 段,每段长度为
,这个划分的代价为字典序最大的划分子段。
求所有划分方案下,字典序最小的划分代价。
题目太难描述了自己看题吧。
正解
环状字符串按照套路先倍长,然后枚举起始位置就能枚举到所有的划分了。
现在问题就是如何快速求出一个划分的代价。
考虑后缀排序,求出每一个后缀的排名。
然后一个划分的代价其实就是区间(并不连续,类似于区间 )的排名取 max。
这个可以用线段树或者单调队列做,时间复杂度 /
。
总复杂度 。
吐槽
这题后缀排序怎么卡二分 + 哈希啊,让我这种不会 SA 的人怎么活啊。(我的 SA 是直接蒯 Luogu 题解的
upd : 好像后面重测我的二分 + 哈希又活过来了。
#include <bits/stdc++.h>
#define N 1000005
using namespace std;
int n, m, k, rlim;
int mac[N];
int S, rak[N], sa[N], tax[N], tp[N];
char s[N];
void Qsort() {
for (int i = 0; i <= S; i++) tax[i] = 0;
for (int i = 1; i <= n; i++) tax[rak[i]]++;
for (int i = 1; i <= S; i++) tax[i] += tax[i - 1];
for (int i = n; i >= 1; i--) sa[ tax[rak[tp[i]]]-- ] = tp[i];
}
void SuffixSort() {
S = 75;
for (int i = 1; i <= n; i++) rak[i] = s[i] - '0' + 1, tp[i] = i;
Qsort();
for (int w = 1, p = 0; p < n; S = p, w <<= 1) {
p = 0;
for (int i = 1; i <= w; i++) tp[++p] = n - w + i;
for (int i = 1; i <= n; i++) if (sa[i] > w) tp[++p] = sa[i] - w;
Qsort();
swap(tp, rak);
rak[sa[1]] = p = 1;
for (int i = 2; i <= n; i++)
rak[sa[i]] = (tp[sa[i - 1]] == tp[sa[i]] && tp[sa[i - 1] + w] == tp[sa[i] + w]) ? p : ++p;
}
}
int que[N];
int main() {
scanf("%d %d %s", &n, &m, s + 1), k = n / m;
for(int i = 1; i <= n; ++i)
s[i + n] = s[i];
n <<= 1, rlim = n - m + 1;
SuffixSort();
n >>= 1;
for(int i = 1; i <= m; ++i) {
int hd = 1, tl = 0;
for(int j = i; j <= rlim; j += m) {
while(hd <= tl && j - que[hd] >= n) ++hd;
while(hd <= tl && rak[j] > rak[que[tl]]) --tl;
que[++tl] = j;
if(j - m * (k - 1) > 0)
mac[j - m * (k - 1)] = rak[que[hd]];
}
}
mac[0] = N;
int mpos = 0;
for(int i = 1; i <= n; ++i)
if(mac[i] < mac[mpos])
mpos = i;
int str = sa[mac[mpos]];
for(int i = 0; i < m; ++i)
putchar(s[str + i]);
putchar('\n');
return 0;
} 
京公网安备 11010502036488号