又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。
应用
- 串的快速检索
给出N个单词组成的熟词表,以及一篇全用小写英文书写的文章,请你按最早出现的顺序写出所有不在熟词表中的生词。 - 串的排序
给定N个互不相同的仅由一个单词构成的英文名,让你将他们按字典序从小到大输出。用字典树进行排序,采用数组的方式创建字典树,这棵树的每个结点的所有儿子很显然地按照其字母大小排序。对这棵树进行先序遍历即可。 - 最长公共前缀
对所有串建立字典树,对于两个串的最长公共前缀的长度即他们所在的结点的公共祖先个数,于是,问题就转化为当时公共祖先问题。
字典树通常用next指针数组指向子结点,构造整棵树;但是在比赛中为了避免使用指针出错可以使用数组模拟指针的存储方式。
结点的结构体:
struct trie{ int next[maxn];//maxn = 字符种类的个数 int v;//记录该字符出现次数 }t[maxm];//maxm = 结点个数
插入的过程就是建树的过程,如果当前当前前缀的子结点中已经出现此时读到的字符,将前缀移动到该子结点,并将这一前缀出现的次数加一;反之,在当前前缀后建立新的对应这一字符的子结点,并将前缀移动到该子结点,赋出现次数的初始值为1。
用根结点(trie[0])的v记录整棵树的结点个数,新增结点即++trie[0].v;
代码:
void insert_trie(char *s) { int len = strlen(s); int now = 0; for(int i=0;i<len;i++) { int key = s[i] - '0'; //key的值由字符串字符类型决定 if(t[now].next[key] != -1) { now = t[now].next[key]; t[now].v ++; } else { t[now].next[key] = ++t[0].v; now = t[now].next[key]; t[now].v = 1; memset(t[now].next, -1, sizeof(t[now].next)); } } }
查找即在当前的书中查找公共前缀,代码:
int find_trie(char *s) { int len = strlen(s); int now = 0,ret = 0; for(int i=0;i<len;i++) { int key = s[i] - '0'; if(t[now].next[key] != -1) { now = t[now].next[key]; ret = t[now].v; } else return 0; } return ret; }
字典树的初始化,将所有trie[0].v个结点的v全都还原为0,next数组初始化为-1。
代码:
void init() { for(int i=0;i<=t[0].v;i++) { if(i) t[i].v = 0; memset(t[i].next, -1, sizeof(t[i].next)); } t[0].v = 0; }
题
HDU 1251统计难题/一个裸的模版题/
#include <iostream> #include <cstdio> #include <cmath> #include <cstring> #include <algorithm> #include <string> #include <vector> #include <queue> #include <stack> #include <set> #include <map> #define INF 0x3f3f3f3f #define EPS 0.00000001 #define lowbit(x) (x&(-x)) using namespace std; typedef long long ll; const int maxn = 26; typedef struct Trie Trie; typedef struct Trie* ptr; struct Trie { ptr next[maxn]; int v; //表示一个字典树到此有多少相同前缀的数目 }; ptr root; void init() { if(root == NULL) { root = (ptr) malloc (sizeof(Trie)); root -> v = 1; for(int j=0;j<maxn;j++) root -> next[j] = NULL; } } void Insert(char *s) { int len = strlen(s); ptr now = root; for(int i=0;i<len;i++) { int key = s[i] - 'a'; if(now -> next[key] != NULL) { now -> next[key] -> v ++; now = now -> next[key]; } else { ptr tmp = (ptr) malloc (sizeof(Trie)); tmp -> v = 1; for(int j=0;j<maxn;j++) tmp -> next[j] = NULL; now -> next[key] = tmp; now = tmp; } } } int findTrie(char *s) { int len = strlen(s), ret = 0; ptr now = root; for(int i=0;i<len;i++) { int key = s[i] - 'a'; if(now -> next[key] != NULL) { now = now -> next[key]; ret = now -> v; } else { return 0; } } return ret; } int main() { init(); char s[15]; int flag = 0; while(gets(s) != NULL) { if(strlen(s) == 0) { flag = 1; continue; } if(!flag) Insert(s); else cout << findTrie(s) << endl; } }
HDU 1671Phone List/要加初始化/
#include <iostream> #include <cstdio> #include <cmath> #include <cstring> #include <algorithm> #include <string> #include <vector> #include <queue> #include <stack> #include <set> #include <map> #define INF 0x3f3f3f3f #define EPS 0.00000001 #define lowbit(x) (x&(-x)) using namespace std; typedef long long ll; const int maxn = 10; const int maxm = 100005; struct trie{ int next[maxn]; int v; }t[maxm]; char s[maxm][15]; void init() { for(int i=0;i<=t[0].v;i++) { if(i) t[i].v = 0; memset(t[i].next, -1, sizeof(t[i].next)); } t[0].v = 0; } void ins(char *s) { int len = strlen(s); int now = 0; for(int i=0;i<len;i++) { int key = s[i] - '0'; if(t[now].next[key] != -1) { now = t[now].next[key]; t[now].v ++; } else { t[now].next[key] = ++t[0].v; now = t[now].next[key]; t[now].v = 1; memset(t[now].next, -1, sizeof(t[now].next)); } } } int findtrie(char *s) { int len = strlen(s); int now = 0,ret = 0; for(int i=0;i<len;i++) { int key = s[i] - '0'; if(t[now].next[key] != -1) { now = t[now].next[key]; ret = t[now].v; } else return 0; } return ret; } int main() { int T; scanf("%d",&T); while(T--) { init(); int n; scanf("%d",&n); for(int i=0;i<n;i++) { scanf("%s",s[i]); ins(s[i]); } int flag = 0; for(int i=0;i<n;i++) if(findtrie(s[i]) > 1) { flag = 1; break; } printf(flag ? "NO " : "YES "); } }