了解这个数据结构以前咱们须要了解它能被用来作什么ios
字典树又称单词查找树,Tire树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不只限于字符串),因此常常被搜索引擎系统用于文本词频统计。它的优势是:利用字符串的公共前缀来减小查询时间,最大限度地减小无谓的字符串比较,查询效率比哈希树高。web
说到底,字典树就是用来查询公共前缀的一个工具,延伸的话能够用来进行串匹配,词频统计等,也是学AC自动机
的前置技能.数组
所谓的字典树,其实就是一个n叉树数据结构
咱们对于每一个字母,若是有公共前缀的,咱们找到它的前缀,在后面不一样的部分创建不一样的子节点,好比说apple
,appear
,appxy
三个不一样的单词,公共前缀为app
,因此建树以下:
若是下面有一个单词为apzl
的话,建树以下
也就是只注意前缀相同的部分,后面即便有同样的字母也从新创建子节点app
那么问题来了,单词的第一个字母不止A
啊,应该怎么办?
其实不难,学过二分图的应该能想出来:设置一个超级源点,咱们在第一个字母上面再设一个超级源点,这样计算的时候不考虑他就好了,这样第一层就能够和下面的子节点同样创建了
如图所示(ps:此图有误,apply
应该为appxy
)
svg
又是喜(sang)闻(xin)乐(bing)见(kuang)的代码环节了
我的因为ACM的缘由,就只放数组实现的板子了,(反正懂原理了指针版的也挺简单的)工具
因为数组不能动态开内存,因此咱们就得采用模拟的形式了,这里其实用了一点并查集的思想,各位客官看下图
因为不能动态分配内存,同时字典树又是比较耗费空间的,因此咱们的内存分配尽量大,开一个二维数组tire[maxn][26]
,而后tire[i][j] = k 表明编号为i
的节点的第j
个孩子是编号为k
的节点,这里的j
一般指当前位的字母A-Z
而后关于编号,咱们这里的存树方式是:若是要生成新节点,则编号++,不然编号不动,因此如上图,APPLY
的对应编号应该为1,2,3,4,10,11
;
同时有:搜索引擎
tire[1]['A'-'A'] = 2; tire[2]['P'-'A'] = 3; tire[3]['P'-'A'] = 10; tire[10]['X'-'A'] = 11; tire[11]['Y'-'A'] = 0;
这样,查找的时候利用并查集的思想不断向下查找便可spa
代码以下3d
/*头文件能够忽略,只是一些经常使用的宏*/ #include <map> #include <queue> #include <cstdlib> #include <cmath> #include <cstdio> #include <string> #include <cstring> #include <fstream> #include <iostream> #include <sstream> #include <algorithm> #define _mem(a,b) memset(a,0,(b+3)<<2) #define fori(a) for(int i=0;i<a;i++) #define forj(a) for(int j=0;j<a;j++) #define ifor(a) for(int i=1;i<=a;i++) #define jfor(a) for(int j=1;j<=a;j++) #define mem(a,b) memset(a,b,sizeof(a)) #define IN freopen("in.txt","r",stdin) #define OUT freopen("out.txt","w",stdout) #define IO do{\ ios::sync_with_stdio(false);\ cin.tie(0);\ cout.tie(0);}while(0) #define mp(a,b) make_pair(a,b); using namespace std; typedef long long ll; const int maxn = 1e5; const int INF = 0x3f3f3f3f; const int inf = 0x3f; const double EPS = 1e-7; const double Pi = acos(-1); const int MOD = 1e9+7; int Tire[maxn][26]; char str[2000005]; bool v[maxn]; string s; int cnt = 1; //建树,每输入一个单词到s里面就调用_insert()就好 void _insert(){ int root = 0; fori(s.size()){ int next = s[i] - 'A'; if(!Tire[root][next]) Tire[root][next] = ++cnt; root = Tire[root][next]; } v[root] = true;//这里用了一个标记数组表示该点存在一个完整的单词,好比说`app`和`apple` //在最后一个`p`的位置就会被标记true } //查找最长公共前缀 int _find(char bufs[],int leng){ int root = 0; int cns = 0; int next; int res = 0; fori(leng){ next = bufs[i] - 'A'; if(Tire[root][next] == 0) break; root = Tire[root][next]; cns++; if(v[root]) res = cns; } return res; }