瞭解這個數據結構以前咱們須要瞭解它能被用來作什麼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; }