UVALive 11732 (strcmp() Anyone?)[Trie(左儿子右兄弟表示法)]

·· / ·– ·· ·-·· ·-·· / ·–· · ·-· ··· ·· ··· - / ··- -· - ·· ·-·· / ·· / ·– ·· -·
这题是刘汝佳老师《算法竞赛入门经典-训练指南》上字符串一节的例题,下文的思路直接来自书上的解析,代码来自代码仓库(需翻墙)
把所有单词插入一棵Trie树,则在深度为depth的分叉点的任何两个字符串都需要比较2*depth-1次。
因为字符集比较大,需要用左儿子右兄弟法保存Trie。这种方法类似于保存图的邻接表。

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
#include <cstdio>
#include <cstring>
#include <algorithm>
const int maxn = 4000 * 1000 + 10;
const int maxl = 1000 + 10;

struct Trie {
int head[maxn];
int next[maxn];
char ch[maxn];
int tot[maxn];
int sz;
long long ans;
void clear() { sz = 1; tot[0] = head[0] = next[0] = 0; }
void insert (const char *s) {
int u = 0, v, n = strlen(s);
tot[0]++;
for (int i = 0; i <= n; i++) {
bool found = false;
for (v = head[u]; v != 0; v = next[v])
if (ch[v] == s[i]) {
found = true;
break;
}
if (!found) {
v = sz++;
tot[v] = 0;
ch[v] = s[i];
next[v] = head[u];
head[u] = v;
head[v] = 0;
}
tot[v]++;
u = v;
}
}
void dfs(int depth, int u) {
if (head[u] == 0)
ans += tot[u] * (tot[u] - 1) * depth;
else {
int sum = 0;
for (int v = head[u]; v != 0; v = next[v])
sum += tot[v] * (tot[u] - tot[v]);
ans += sum / 2 * (2 * depth + 1);
for (int v = head[u]; v != 0; v = next[v])
dfs(depth + 1, v);
}
}
long long count() {
ans = 0;
dfs(0, 0);
return ans;
}
}trie;

int n;
char word[maxl];

int main() {
int cas = 1;
while (scanf("%d", &n) == 1 && n) {
trie.clear();
for (int i = 0; i < n; i++) {
scanf("%s", word);
trie.insert(word);
}
printf("Case %d: %lld\n", cas++, trie.count());
}
return 0;
}