「JSOI2019」节日庆典

#3103. 「JSOI2019」节日庆典

Summarize

对于给定字符串 $S$,求 $S$ 的每个前缀的最小循环后缀。

$|S|\le 3000000$

Solution

考虑对 $S$ 的每个前缀维护一个集合 $T$,包含该前缀的最小后缀位置。将这些最小后缀位置称为“候选点”。

记正在处理的前缀为 $k$,那么这个集合具有以下性质:

  1. 若 $i,j\in T(i<j)$,那么 $S[j..k]=S[i..i+k-j]$,即后缀 $j$ 为后缀 $i$ 的前缀。

  2. 若 $i,j\in T(i<j)$,且 $S[j..k]=S[i..i+k-j]$,若 $i+k-j\ge j$,则 $j$ 不会成为最优位置。

    简单说明一下性质 2。容易发现后缀 $i$ 的形式为 $ABABA$,后缀 $j$ 为 $ABA$。若循环后缀的下一位较小,则 $A$ 为最优后缀;反之,则 $ABABA$ 为最优位置;$ABA$ 不会成为最优位置。

维护出这个集合后,发现对于 $S$ 的每个前缀,集合 $T$ 的大小不超过 $\log k$。暴力枚举集合中所有候选点并比较即可。

具体比较方法可以利用性质 1,转化为后缀与原串的比较。在进行后缀与原串的比较时可以利用 扩展 KMP 算法中求出的 $\text{next}$ 数组进行 $O(1)$ 复杂度比较。

细节处理比较麻烦。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include <bits/stdc++.h>
using namespace std;
#define next next233

const int MAXN = 3e6 + 5;

char s[MAXN];
int next[MAXN];
int st1[MAXN], st2[MAXN], cnt1, cnt2;
int *x = st1, *y = st2;

void exKMP_pre(char *T) {
int n = strlen(T);
next[0] = n;
int p = 0;
while (p + 1 < n && T[p] == T[p + 1]) p++;
next[1] = p;
int k = 1;
for (int x = 2; x < n; ++x) {
p = k + next[k] - 1;
int l = next[x - k];
if (x + l <= p) next[x] = l;
else {
int j = p - x + 1;
if (j < 0) j = 0;
while (x + j < n && T[x + j] == T[j]) j++;
next[x] = j;
k = x;
}
}
}

int cmp(int p, int q, int k) {
int t = p + k - q + 1;
if (next[t] < k - t + 1) {
if (s[next[t]] < s[t + next[t]]) return 1;
else return -1;
}
t = q - p;
if (next[t] < p) {
if (s[next[t]] < s[t + next[t]]) return -1;
else return 1;
}
return 0;
}

int main() {
scanf("%s", s);
exKMP_pre(s);
int n = strlen(s);
for (int i = 0; i < n; ++i) {
x[++cnt1] = i;
for (int k = 1; k <= cnt1; ++k) {
int q = x[k];
if (cnt2 == 0) {
y[++cnt2] = q;
continue;
}
int p = y[cnt2];
if (s[i] > s[p + i - q]) continue;
if (s[i] < s[p + i - q]) {
y[cnt2 = 1] = q;
continue;
}
if (s[i] == s[p + i - q]) {
if (p + i - q < q) y[++cnt2] = q;
continue;
}
}
swap(x, y);
cnt1 = cnt2, cnt2 = 0;
int ans = x[1];
for (int k = 2; k <= cnt1; ++k) {
if (cmp(ans, x[k], i) == 1) ans = x[k];
}
printf("%d ", ans + 1);
}
return 0;
}
# KMP

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×