\(\mathscr{Description}\)

  Link.

  给定字符串 \(S\),求 \(S\) 的每个前缀的最小表示法起始下标(若有多个,取最小的)。

  \(|S|\le3\times10^6\)。

\(\mathscr{Solution}\)

  注意到一个显然的事实,对于某个前缀 \(S[:i]\) 以及两个起始下标 \(p,q\),若已有 \(S[p:i]<S[q:i]\),那么在所有的 \(j>i\) 中,都有 \(S[p:j]<S[q:j]\)。换言之,最终 \(i\) 的答案 \(r_i\) 必然满足 \(r_i\in B_i=\arg\min_{p\le i}\{S[p:i]\}\),同时有 \(B_{i+1}\subseteq B_i\cup\{i+1\}\),故我们可以通过去除 \(B_i\cup\{i+1\}\) 中不优秀的起始位置来得到 \(B_{i+1}\)。

  可惜,\(|B_m|=\mathcal O(m)\) 的,需要进一步精简。这提示我们反思“\(S[p:i]\) 取最小”是否是“\(p\) 可能成为 \(r_i\)”的充要条件。答案是否定的。考虑后缀 \(S[p:i]\) 和 \(S[q:i]~(p<q)\),若 \(p,q\in B_i\),有 \(S[p:p+i-q+1]=S[q:i]\),即 \(S[q:i]\) 是 \(S[p:i]\) 的一个 border。由 border 的相关性质,自然地想到研究 \(|S[q:i]|>\frac{|S[p:i]|}{2}\) 的情况,此时 \(S[p:i]\) 有周期 \(T=|S[p:i]|-|S[q:i]|\)。取 \(S[r:i]=S[i-T+1:i]\),可以说明:\(S[p:i]\) 不同时大于 \(S[q:i]\) 和 \(S[r:i]\)。如图:

  若我们想让 \(S[p:i]<S[q:i]\),就需要在后缀加一个字符 \(S_{i+1}=x\)(红点),使得 \(x>y\)(蓝点),但一旦有 \(y<x\),就能让最后一个周期 \(S[r:i]\) 带上红点一路走到 \(S[p:i]\) 的开头,得到 \(S[r:i+1]<S[p:i+1]\),故结论成立。 \(\square\)

  据此维护出不存在满足上述 \(p,q\) 关系的新集合 \(B_i'\subseteq B_i\),显然 \(|B_m'|=\mathcal O(\log m)\),所以维护总过程是 \(\mathcal O(n\log n)\) 的。

  此外,求答案 \(r_i\) 时,仍然有必要枚举 \(B_i'\) 中的每个下标取优。利用前缀相等的性质,发现我们仅需要完成 \(S\) 子串与 \(S\) 前缀的快速比较,那么用 Z-function 可以做到 \(\mathcal O(1)\)。综上,最终复杂度为 \(\mathcal O(n\log n)\)。


  这个时候就有神要问了哈,你怎么不写 \(\mathcal O(n)\) 的做法?

  重新审视一下这个问题,很自然联想到描述“最小表示法等于自身”的 Lyndon Word,而 Duval 算法提供了一个 \(\mathcal O(n)\) 考察 Lyndon Word 的思路,所以我们可以尝试把问题向 Lyndon 的方向转化。设串 \(s=S[:i]\) 的 Lyndon 分解为

\[s=w_1^{k_1}w_2^{k_2}\cdots w_m^{k_m},
\]

其中 \(w_1>w_2>\cdots>w_m\)。我们先把 \(\mathcal O(n\log n)\) 做法中的关键性结论重新描述:显然最小表示的后缀在一个 Lyndon Word 的开头,而若取出了 \(w_i^kw_{i+1}^{k_{i+1}}\cdots w_m^{k_m}\),我们则能断言:\(k=k_i\)。

  现在套上 Duval 的样子,设 \(s=s'u^ku'\),其中 \(u^k\) 是 Lyndon Word,\(u'\) 是 \(u\) 未扩展完的前缀。前面的 \(s'\) 也不重要了,我们记 \(t=u^ku'\) 来研究,考虑加入字符 \(c=S_{|s|+1}\):

  • \(t_{|u'|+1}<c\),答案 \(p_{|s|+1}\) 显然为 \(t_1\) 在原串对应的下标;
  • \(t_{|u'|+1}>c\),考虑 Duval 的过程,我们得先把一段前缀循环划为 Lyndon,以后再更新 \(p_{|s|+1}\);
  • \(t_{|u'|+1}=c\),继续吻合循环节,两种可能优解:\(p=i\) 和 \(p\) 取 \(u\) 的最优起始位置在 \(u'\) 中对应的位置。第二种情况考虑到虽然 \(u\) 为 Lyndon Word,但 \(S\) 的前缀是有可能小于 \(u\) 的后缀的。还是用上文 Z-function 的方法支持 \(\mathcal O(1)\) 比较。

  最终,与 Duval 几乎一样地,我们在 \(\mathcal O(n)\) 的时间内解决了问题。

\(\mathscr{Code}\)

  • \(\mathcal O(n\log n)\):
  1. /*+Rainybunny+*/
  2. #include <bits/stdc++.h>
  3. #define rep(i, l, r) for (int i = l, rep##i = r; i <= rep##i; ++i)
  4. #define per(i, r, l) for (int i = r, per##i = l; i >= per##i; --i)
  5. inline void wint(const int x) {
  6. if (9 < x) wint(x / 10);
  7. putchar(x % 10 ^ '0');
  8. }
  9. const int MAXN = 3e6, MAXG = 100;
  10. int n, z[MAXN + 5];
  11. char s[MAXN + 5];
  12. int gcnt, good[MAXG + 5]; // indices that may be the answer.
  13. inline void calcZ() {
  14. z[1] = n;
  15. for (int i = 2, l = 0, r = 0; i <= n; ++i) {
  16. if (i <= r) z[i] = std::min(z[i - l + 1], r - i + 1);
  17. while (i + z[i] <= n && s[i + z[i]] == s[1 + z[i]]) ++z[i];
  18. if (i + z[i] - 1 > r) r = i + z[l = i] - 1;
  19. }
  20. }
  21. inline void adapt(const int k) {
  22. auto compare = [&](const int u, const int v)->int {
  23. return u < v ?
  24. (s[u + k - v] == s[k] ? 0 : s[u + k - v] < s[k] ? -1 : 1)
  25. : (s[k] == s[v + k - u] ? 0 : s[k] < s[v + k - u] ? -1 : 1);
  26. };
  27. static int tmp[MAXG + 5]; rep (i, 1, gcnt) tmp[i] = good[i];
  28. int ocnt = gcnt, pst = good[gcnt]; good[gcnt = 1] = pst;
  29. per (i, ocnt - 1, 1) {
  30. if (int t = compare(pst, tmp[i]); t > 0) {
  31. good[gcnt = 1] = pst = tmp[i];
  32. } else if (!t) {
  33. pst = tmp[i];
  34. while (gcnt && k - good[gcnt] + 1 << 1 > k - pst + 1) --gcnt;
  35. good[++gcnt] = pst;
  36. }
  37. }
  38. std::reverse(good + 1, good + gcnt + 1);
  39. }
  40. inline int best(const int k) {
  41. // compare S[l:r] with S[:r-l+1].
  42. auto compare = [](const int l, const int r)->int {
  43. if (z[l] >= r - l + 1) return 0;
  44. return s[l + z[l]] < s[z[l] + 1] ? -1 : 1;
  45. };
  46. int ans = good[1];
  47. rep (i, 2, gcnt) {
  48. int cur = good[i];
  49. if (int f1 = compare(ans + k - cur + 1, k); f1 > 0) ans = cur;
  50. else if (!f1) {
  51. int f2 = compare(cur - ans + 1, cur - 1); // cur <> ans.
  52. if (f2 < 0) ans = cur;
  53. }
  54. }
  55. return ans;
  56. }
  57. int main() {
  58. scanf("%s", s + 1), n = strlen(s + 1), calcZ();
  59. rep (i, 1, n) {
  60. good[++gcnt] = i, adapt(i);
  61. wint(best(i)), putchar(i < n ? ' ' : '\n');
  62. }
  63. return 0;
  64. }
  • \(\mathcal O(n)\)(目前洛谷最优解):
  1. /*+Rainybunny+*/
  2. #include <bits/stdc++.h>
  3. #define rep(i, l, r) for (int i = l, rep##i = r; i <= rep##i; ++i)
  4. #define per(i, r, l) for (int i = r, per##i = l; i >= per##i; --i)
  5. inline void wint(const int x) {
  6. if (9 < x) wint(x / 10);
  7. putchar(x % 10 ^ '0');
  8. }
  9. const int MAXN = 3e6;
  10. int n, z[MAXN + 5], ans[MAXN + 5];
  11. char s[MAXN + 5];
  12. inline void calcZ() {
  13. z[1] = n;
  14. for (int i = 2, l = 0, r = 0; i <= n; ++i) {
  15. if (i <= r) z[i] = std::min(z[i - l + 1], r - i + 1);
  16. while (i + z[i] <= n && s[i + z[i]] == s[1 + z[i]]) ++z[i];
  17. if (i + z[i] - 1 > r) r = i + z[l = i] - 1;
  18. }
  19. }
  20. inline void duval() {
  21. auto compare = [](const int u, const int v, const int k)->int {
  22. int p = u + k - v + 1, q = k - p + 2;
  23. if (z[p] < k - p + 1) {
  24. return s[p + z[p]] < s[1 + z[p]] ? -1 : 1;
  25. } else {
  26. return z[q] < u ? s[q + z[q]] < s[1 + z[q]] ? 1 : -1 : 0;
  27. }
  28. };
  29. for (int i = 1; i <= n;) {
  30. int j = i + 1, k = i;
  31. if (!ans[i]) ans[i] = i;
  32. while (j <= n && s[k] <= s[j]) {
  33. if (s[k] < s[j]) {
  34. if (!ans[j]) ans[j] = i;
  35. k = i;
  36. } else {
  37. if (!ans[j]) {
  38. if (ans[k] < i) ans[j] = i;
  39. else {
  40. ans[j] = compare(i, j - k + ans[k], j) <= 0 ?
  41. i : j - k + ans[k];
  42. }
  43. }
  44. ++k;
  45. }
  46. ++j;
  47. }
  48. i += (j - i) / (j - k) * (j - k);
  49. }
  50. }
  51. int main() {
  52. n = fread(s + 1, 1, MAXN + 3, stdin);
  53. while (s[n] < 'a' || 'z' < s[n]) --n;
  54. calcZ(), duval();
  55. rep (i, 1, n) wint(ans[i]), putchar(i < n ? ' ' : '\n');
  56. return 0;
  57. }

Solution -「JSOI 2019」「洛谷 P5334」节日庆典的更多相关文章

  1. 「区间DP」「洛谷P1043」数字游戏

    「洛谷P1043」数字游戏 日后再写 代码 /*#!/bin/sh dir=$GEDIT_CURRENT_DOCUMENT_DIR name=$GEDIT_CURRENT_DOCUMENT_NAME ...

  2. Solution -「洛谷 P4372」Out of Sorts P

    \(\mathcal{Description}\)   OurOJ & 洛谷 P4372(几乎一致)   设计一个排序算法,设现在对 \(\{a_n\}\) 中 \([l,r]\) 内的元素排 ...

  3. Solution -「POI 2010」「洛谷 P3511」MOS-Bridges

    \(\mathcal{Description}\)   Link.(洛谷上这翻译真的一言难尽呐.   给定一个 \(n\) 个点 \(m\) 条边的无向图,一条边 \((u,v,a,b)\) 表示从 ...

  4. Solution -「APIO 2016」「洛谷 P3643」划艇

    \(\mathcal{Description}\)   Link & 双倍经验.   给定 \(n\) 个区间 \([a_i,b_i)\)(注意原题是闭区间,这里只为方便后文描述),求 \(\ ...

  5. 「洛谷5290」「LOJ3052」「十二省联考 2019」春节十二响【启发式合并】

    题目链接 [洛谷传送门] [LOJ传送门] 题目大意 给定一棵树,每次选取树上的一个点集,要求点集中的每个点不能是另一个点的祖先,选出点集的代价为点集中权值最大点的权值,问将所有点都选一遍的最小代价为 ...

  6. 「洛谷5283」「LOJ3048」「十二省联考2019」异或粽子【可持久化01trie+优先队列】

    题目链接 [洛谷传送门] [LOJ传送门] 题目大意 让你求区间异或和前\(k\)大的异或和的和. 正解 这道题目是Blue sky大佬教我做的(祝贺bluesky大佬进HA省A队) 我们做过某一些题 ...

  7. 「洛谷4197」「BZOJ3545」peak【线段树合并】

    题目链接 [洛谷] [BZOJ]没有权限号嘤嘤嘤.题号:3545 题解 窝不会克鲁斯卡尔重构树怎么办??? 可以离线乱搞. 我们将所有的操作全都存下来. 为了解决小于等于\(x\)的操作,那么我们按照 ...

  8. 「洛谷3338」「ZJOI2014」力【FFT】

    题目链接 [BZOJ] [洛谷] 题解 首先我们需要对这个式子进行化简,否则对着这么大一坨东西只能暴力... \[F_i=\sum_{j<i} \frac{q_iq_j}{(i-j)^2}-\s ...

  9. 「BZOJ2733」「洛谷3224」「HNOI2012」永无乡【线段树合并】

    题目链接 [洛谷] 题解 很明显是要用线段树合并的. 对于当前的每一个连通块都建立一个权值线段树. 权值线段树处理操作中的\(k\)大的问题. 如果需要合并,那么就线段树暴力合并,时间复杂度是\(nl ...

随机推荐

  1. Nginx 负载均衡服务器的下载与安装 【window10】

    1.前言 Nginx是什么? 找了下资料,解释 : Nginx("engine x")是一款是由俄罗斯的程序设计师Igor Sysoev所开发高性能的 Web和 反向代理 服务器, ...

  2. 经典定长指令-修改EIP

    1.0x70~0x7F EIP无法像通用寄存器那样用mov来修改,只能通过类似于jz,JNB,JNE JBE,call等的跳转指令来进行修改 条件跳转,后跟一个字节立即数的偏移(有符号),共两个字节. ...

  3. FFT 傅里叶万岁

    FFT --- Fast Foulier Transformation 以 $O(n \log n)$ 的速度计算 $\forall k=1,2,\dots,n, c[k]=\sum\limits_{ ...

  4. 《剑指offer》面试题50. 第一个只出现一次的字符

    问题描述 在字符串 s 中找出第一个只出现一次的字符.如果没有,返回一个单空格. 示例: s = "abaccdeff" 返回 "b" s = "&q ...

  5. [Jetson Nano]Jetson Nano快速入门

    NVIDIAJetsonNano开发套件是适用于制造商,学习者和开发人员的小型AI计算机.相比Jetson其他系列的开发板,官方报价只要99美金,可谓是相当有性价比.本文如何是一个快速入门的教程,主要 ...

  6. 【刷题-LeetCode】216. Combination Sum III

    Combination Sum III Find all possible combinations of k numbers that add up to a number n, given tha ...

  7. 【刷题-LeetCode】150 Evaluate Reverse Polish Notation

    Evaluate Reverse Polish Notation Evaluate the value of an arithmetic expression in Reverse Polish No ...

  8. linu查看系统用户与显示命令行提示符格式信息

    目录 一:查看系统用户whoami 二:显示命令行提示符格式信息变量 一:查看系统用户whoami whoami : 当前窗口登录的用户 who : 当前用户登录系统的终端 作用: 显示当前用户登录了 ...

  9. Spring系列9:基于注解的Spring容器配置

    写在前面 前面几篇中我们说过,Spring容器支持3种方式进行bean定义信息的配置,现在具体说明下: XML:bean的定义和依赖都在xml文件中配置,比较繁杂. Annotation-based ...

  10. AtCoder ABC 215 简要题解

    A - B 模拟 C 可以直接爆搜,也可以写逐位确定的多项式复杂度算法,使用多重组合式求随意乱排的方案数. D 首先对 \(A\) 所有数暴力分解质因数,然后把遇到过的质因数打上标记. 接下来再对 \ ...