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 }