题解 [牛客挑战赛39B] 密码系统
密码系统
https://ac.nowcoder.com/acm/contest/5157/B
题目描述
给定一个长度为 的环状字符串。
将这个字符串划分成 段,每段长度为
,这个划分的代价为字典序最大的划分子段。
求所有划分方案下,字典序最小的划分代价。
题目太难描述了自己看题吧。
正解
环状字符串按照套路先倍长,然后枚举起始位置就能枚举到所有的划分了。
现在问题就是如何快速求出一个划分的代价。
考虑后缀排序,求出每一个后缀的排名。
然后一个划分的代价其实就是区间(并不连续,类似于区间 )的排名取 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; }