模板 后缀排序(有待完善)

UOJ传送门

题目大意

给出一个字符串(由大小字母和英文组成),把后缀排序,输出后缀数组和$height$数组。

解析

我不会后缀数组啊。

但是我屯了一个SAM的板子,听说用这个东西可以做。

然后我就想,能不能把后缀树搞出来,有了后缀树我们就可以随便dfs一下就行了吧。

但是怎么构造呢?

SAM好像有两部分来着?一个是DAWG,一个是Parent树?

还是想不到,那么:

我们可以考虑上网搜题解。

——hh

题解告诉我们,反串的Parent树就是后缀树?

那就这样吧。

然后有了SA数组之后,我们要求$height_i=LCP(S[SA_{i-1}:n],S[SA_i:n]$对吧。

考虑一个辅助数组:
$$
\begin{align*}
h_i&=height_{rank_i}\\
&=LCP(S[SA_{rank_i-1}:n],S[SA_{rank_i}:n])\\
&=LCP(S[SA_{rank_i-1}:n],S[i:n])
\end{align*}
$$
然后我听说有一个结论,就是说$h_{i-1}-1\le h_i$,具体我不太会证。

然后就可以用这个不太又没的单调性来求出$h_i$,进而求出$height$了。

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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#define DREP(i, s, e) for(int i = s; i >= e ;i--)
#define REP(i, s, e) for(int i = s; i <= e ;i++)

#define DEBUG fprintf(stderr, "Passing [%s] in Line %d\n", __FUNCTION__, __LINE__)
#define chkmax(a, b) a = max(a, b)
#define chkmin(a, b) a = min(a, b)

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;
const int maxn = 1e5 + 10, maxnode = maxn << 1;

const int siz = 26;

int Max[maxnode], id[maxnode], ch[maxnode][siz], ne[maxnode], start, last, cur;
int newnode(int _Max = 0, int _id = 0)
{
++cur;
id[cur] = _id;
Max[cur] = _Max;
return cur;
}
void init() {start = last = newnode();}

int n;
char s[maxn];

void extend(int c, int _id)
{
int u = newnode(Max[last] + 1, _id), v = last;
for (; v && !ch[v][c]; v = ne[v]) ch[v][c] = u;
if (!v) ne[u] = start;
else if (Max[v] + 1 == Max[ch[v][c]]) ne[u] = ch[v][c];
else
{
int Old = ch[v][c], New = newnode(Max[v] + 1);
copy(ch[Old], ch[Old] + siz, ch[New]);
ne[New] = ne[Old];
ne[u] = ne[Old] = New;
for (; v && ch[v][c] == Old; v = ne[v]) ch[v][c] = New;
}
last = u;
}
bool vis[maxnode];
struct state{int x, y, z;}q[maxnode];int top;
int cnt[siz], rk[maxnode];

int bg[maxnode], nex[maxnode], to[maxnode], e;
void add(int x, int y)
{
e++;
to[e] = y;
nex[e] = bg[x];
bg[x] = e;
}
int sa[maxn], _rank[maxn], height[maxn], h[maxn], dfn;
void dfs(int x)
{
if (id[x]) sa[++dfn] = id[x];
for (int i = bg[x]; i ;i = nex[i]) dfs(to[i]);
}
int main()
{
#ifdef CraZYali
freopen("35.in", "r", stdin);
freopen("35.out", "w", stdout);
#endif
init();
scanf("%s", s + 1);n = strlen(s + 1);
DREP(i, n, 1) extend(s[i] - 'a', i);
vis[1] = 1;
REP(i, 2, cur)
if (!vis[i] && id[i])
for (int j = i, pos = n; !vis[j]; vis[j] = 1, j = ne[j], --pos)
{
pos = pos - Max[j] + Max[ne[j]] + 1;
q[++top] = state{ne[j], j, s[pos] - 'a'};
}
REP(i, 1, top) cnt[q[i].z]++;
REP(i, 1, siz-1) cnt[i] += cnt[i-1];
DREP(i, top, 1) rk[cnt[q[i].z]--] = i;
DREP(i, top, 1) add(q[rk[i]].x, q[rk[i]].y);
dfs(start);
REP(i, 1, n) printf("%d%c", sa[i], i == n ? '\n' : ' ');
REP(i, 1, n) _rank[sa[i]] = i;
REP(i, 1, n)
{
h[i] = max(0, h[i-1] - 1);
int maxlen = min(n - i + 1, n - sa[_rank[i]-1] + 1);
while (h[i] < maxlen && s[i + h[i]] == s[sa[_rank[i]-1] + h[i]]) h[i]++;
}
REP(i, 1, n) height[_rank[i]] = h[i];
REP(i, 2, n) printf("%d%c", height[i], i == n ? '\n' : ' ');
return 0;
}

评论

Your browser is out-of-date!

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

×