Trie(字典树,前缀树)_模板

Trie

Trie,又经常叫前缀树,字典树等等。

Trie,又称前缀树或字典树,用于保存关联数组,其中的键通常是字符串。与二叉查找树不同,键不是直接保存在节点中,而是由节点在树中的位置决定。一个节点的所有子孙都有相同的前缀,也就是这个节点对应的字符串,而根节点对应空字符串。一般情况下,根节点不保存值,这样可以把几个开头不同的串连在一颗Trie上(如abc,efg)。Trie中的键通常是字符串(所以常叫字典树)。

优点
可以最大限度地减少无谓的字符串比较,故可以用于词频统计和大量字符串排序。

缺点
虽然不同单词共享前缀,但其实trie是一个以空间换时间的算法。其每一个字符都可能包含至多字符集大小数目的指针。

建树
两种建法:
(1) 多叉树:仅字母:26或52,各种字母,数字,符号组合:根据情况算吧,反正需要的空间贼大
(2) 兄弟儿子表示法:用链表,如链式前向星(个人比较喜欢),遍历时间较上一种长

应用
(1)字符串检索
(2)用多叉树建的树可以实现字典序排序
(3)最长公共前缀
(4)AC自动机等会用到

促使我学习Trie的题目:UVA 11732 "strcmp()" Anyone?

并没有UVA链接,其他OJ大概也搜得到

这道题给出一个 strcmp() 函数的实现方式,我们要求的就是判断 ‘==’ 的次数

int strcmp(char *s, char *t)
{
    int i;
    for (i = 0; s[i] == t[i]; i++)
        if (s[i] == 0) return 0;
    return s[i] - t[i];
}
题面

由于要比较最后的 0,那么字符串相等则答案加 2 * strlen(str) + 2,否则加 2 * ptr + 1,ptr为中断位置。

代码
我使用的是兄弟儿子表示法(很显然),时间复杂度的话,不是很慢,还行吧 . . .

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #define CL(X,N) memset((X), (N), sizeof(X))
 5 using namespace std;
 6 typedef long long LL;
 7 const int maxl = 1e3 + 10, maxn = 4e3 + 10;
 8 int n;
 9 char str[maxl];
10 int son[maxn * maxl], bro[maxn * maxl], cnt[maxn * maxl];
11 char trie[maxn * maxl];
12 LL size = 0, ans = 0;
13 
14 inline void Insert(char *s, int len) {
15     int ptr, cur = 0;
16     for(int i = 0; i <= len; ++i) {
17         for(ptr = son[cur]; ptr; ptr = bro[ptr])
18             if(trie[ptr] == s[i]) break;
19         if(!ptr) {
20             ptr = size++;
21             trie[ptr] = s[i];
22             bro[ptr] = son[cur];
23             son[cur] = ptr;
24             cnt[ptr] = 0;
25             son[ptr] = 0;
26         }
27         ans += (cnt[cur] - cnt[ptr]) * (2 * i + 1);
28         if(i == len) {
29             ans += cnt[ptr] * (2 * i + 2);
30             ++cnt[ptr];
31         }
32         ++cnt[cur];
33         cur = ptr;
34     }
35     return ;
36 }
37 
38 inline void Initialize(void) {
39     son[0] = bro[0] = cnt[0] = 0;
40     ans = 0;
41     size = 1;
42     return ;
43 }
44 
45 int main(int argc, char **argv) {
46 #ifdef LOCAL
47     freopen("in.txt", "r", stdin);
48 #endif
49     int len, cas = 0;
50     while(~scanf("%d", &n) && n) {
51         Initialize();
52         for(int i = 0; i < n; ++i) {
53             scanf("%s", str);
54             len = strlen(str);
55             Insert(str, len);
56         }
57         printf("Case %d: %lld", ++cas, ans);
58         putchar(10);
59     }
60     return 0;
61 }
View Code
posted @ 2018-10-06 21:26  Nomaldisk  阅读(492)  评论(0编辑  收藏  举报