字典樹,顧名思義它是棵樹,是棵處理字符串的樹,具體是棵什麼樣的樹呢,咱們能夠舉個栗子:c++
假設如今有四個字符串:ych,yk,devot:數組
那麼這棵樹大概長這個亞子:spa
而圖中加黑的點,也就是每一個單詞的終點;code
主要用於查詢前綴與單詞?blog
而後我們康實現:遞歸
1.插入一個單詞:字符串
首先咱們設置了一個\(trie[i][j]\)數組(這裏設trie樹中全是小寫英文字母,那這樣對於每一個節點,名義上是有26個子節點的。可是顯然咱們不必將空間開的這麼大,由於可能在一組數據中,有些字母是沒有出現過的,因此咱們用多少,開多少),表示以i爲根的子樹裏,第j個字符的編號是多少。get
可能有點抽象,咱們以上圖爲例:it
假設加入單詞的順序是:ych,yk,devotclass
那麼(設1爲根:
\[ trie[1][24]=2;\to y\\ trie[2][2]=3;\ \ \to c\\ trie[3][7]=4;\ \ \to h\\ trie[2][10]=5;\to k\\ trie[1][3]=6;\ \ \to d\\ trie[6][4]=7; \ \ \to e\\ trie[7][14]=8;\to o\\ trie[8][19]=9;\to t \]
從這裏能夠看出,對於一個字母來講,它擁有兩個編號,一個編號是固定不變的,也就是咱們上面數組中的j,而另外一個編號,同一個字母能夠不一樣,取決於加入字典樹的順序,也就是\(trie[i][j]\)的值。
插入時,咱們先判斷是否有這個字母的節點,若是有,就直接在這個節點上繼續操做,若是沒有,新建一個節點,如此下去,一直插入到詞尾
int tot=1; void insert(char *s,int rt/*根*/) { for(int i=0;i<strlen(s);i++) { int x=s[i]-'a'; if(trie[rt][x]==0) trie[rt][x]=++tot;//沒有這個節點,新建 rt=trie[rt][x];//遞歸下去 } vis[rt]=1;//標記這是一個單詞的最後 }
2.查詢操做與插入操做殊途同歸:
從根開始掃描某個字母是否出現過,順着字典樹往下找,若是中途發現沒有某個節點,則證實沒有;
若是找到最後,但vis[rt]=0,證實字典樹中沒有這個單詞,可是有這個前綴;
bool search(char *s,int rt) { for(int i=0;i<strlen(s);i++) { int x=s[i]-'a'; if(trie[rt][x]==0) return 0; rt=trie[rt][x]; } if(vis[rt]) return 1; return 0; }
一道很簡單的板子題:
(記得把數組開大
#include<bits/stdc++.h> using namespace std; int n,m; char a[55]; int vis[1000000],talk[1000000]; int tot=1; int trie[1000000][27]; void insert(char *s,int rt) { for(int i=0;i<strlen(s);i++) { int x=s[i]-'a'; if(trie[rt][x]==0) trie[rt][x]=++tot; rt=trie[rt][x]; } vis[rt]=1; } int search(char *s,int rt) { for(int i=0;i<strlen(s);i++) { int x=s[i]-'a'; if(trie[rt][x]==0) {return 0;} rt=trie[rt][x]; } if(talk[rt]&&vis[rt]) return 2; if(vis[rt]) { talk[rt]=1; return 1; } else { return 0; } } int main() { scanf("%d",&n); int rt=1; for(int i=1;i<=n;i++) { scanf("%s",a); insert(a,rt); } scanf("%d",&m); int bj; for(int i=1;i<=m;i++) { scanf("%s",a); bj=search(a,rt); if(bj==0) printf("WRONG\n"); if(bj==1) printf("OK\n"); if(bj==2) printf("REPEAT\n"); } return 0; }