【字串演算法】字典樹(Trie樹)
什麼是字典樹
基本概念
字典樹,又稱為單詞查詢樹或Tire樹,是一種樹形結構,它是一種雜湊樹的變種,用於儲存字串及其相關資訊。
基本性質
1.根節點不包含字元,除根節點外的每一個子節點都包含一個字元
2.從根節點到某一節點。從根節點到該節點路徑上經過的字元連線起來,就是該節點對應的字串
3.同一個節點的所有子節點包含的字元都不相同
運用方面
典型應用是用於統計,排序和儲存大量的字串(不僅限於字串),經常被搜尋引擎系統用於文字詞頻統計。
優點缺點
字典樹是經典的空間換時間的資料結構,利用字串的公共字首來減少查詢時間,最大限度的減少無謂的字串比較,查詢效率據說比雜湊樹高。
但缺點就很顯然了,就是空間比較大……
舉個栗子
什麼不太瞭解,沒事,讓我們來結合栗子來分析一下!
我們首先讀入四個字串
ba
b
band
abc
在沒有讀入前,我們有一個空空的根節點;
接著我們插入單詞ba;
接著插入單詞b,由於根節點有連向b的子節點,所以只需路徑上的s++就好了;
接著插入單詞bank,ba之前就有,只需s++,而nk需要在ba後新增子節點完成儲存;
最後插入單詞abc;
如何構造字典樹
我們來結合程式一步一步來構造這棵可愛的字典樹吧!!
構造節點
我們需要運用struct來儲存字典樹上每個節點的資訊:
struct node { int s,v[27],val; node() { s=0; memset(v,-1,sizeof(v)); } }t[410000];
s是用來儲存有多少個單詞進過了這個節點,v是用來儲存這個點從a到z的兒子節點分別在哪,而val則是儲存這個節點的權值,至於權值代表什麼,就要依照題目靈活變換了。
構造字典樹
我們先丟擲程式:
int bt(int root) { int len=strlen(a+1);int x=root; for(int i=1;i<=len;i++) { int y=a[i]-'a'+1;//將a^z轉化為1^26 if(t[x].v[y]==-1)t[x].v[y]=++tot; x=t[x].v[y];t[x].s++; } }
首先我們先讀入了一個字串a,它的長度為len;
接著我們對於它的字元進行迴圈處理,當我們處理到這個字串的第i個字元的時候,我們要進行分類討論——
我們用x儲存第i-1個字元在字典樹中的位置;
當我的前一個字元沒有指向我的字元的時候,我就tot++,在字典樹中開創一個新的空間,我就把自己放在這個空間裡;如果我的前一個字元有指向我的字元的子節點時,我就放心地走到那個子節點就好了;最後記得更新x的值為當前處理的子節點的位置,並且s++,表示又多了一個單詞進過了這個節點,以及完成val的修改之類的工作;
i++,進入下一重迴圈!
這樣一棵完整的字典樹就出來了!
模板&&模板題
【caioj 1463】統計字首
題目描述
【題意】
給出很多個字串(只有小寫字母組成)和很多個提問串,統計出以某個提問串為字首的字串數量(單詞本身也是自己的字首).
【輸入格式】
輸入n,表示有n個字串(n<=10000)
接下來n行,每行一個字串,字串度不超過10
輸入m,表示有m個提問(m<=100)
第二部分是一連串的提問,每行一個提問,每個提問都是一個字串.
【輸出格式】
對於每個提問,給出以該提問為字首的字串的數量.
【樣例輸入】
5
banana
band
bee
absolute
acm
4
ba
b
band
abc
【樣例輸出】
2
3
1
0
就是一道裸題,查詢時輸出對應節點的s就好了;
附上程式碼:
#include<bits/stdc++.h>
using namespace std;
struct node
{
int s,v[27];
node()
{
s=0;
memset(v,-1,sizeof(v));
}
}t[410000];
char a[410000];
int i,j,k,m,n,tot,js,jl;
int bt(int root)
{
int len=strlen(a+1);int x=root;
for(int i=1;i<=len;i++)
{
int y=a[i]-'a'+1;
if(t[x].v[y]==-1)t[x].v[y]=++tot;
x=t[x].v[y];t[x].s++;
}
}
int solve(int root)
{
int len=strlen(a+1);int x=root;
for(int i=1;i<=len;i++)
{
int y=a[i]-'a'+1;
if(t[x].v[y]==-1)return 0;
x=t[x].v[y];
}
return(t[x].s);
}
int main()
{
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%s",a+1);
bt(0);
}
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%s",a+1);
printf("%d\n",solve(0));
}
}
結語
通過這篇BLOG相信你已經掌握了Trie樹,那就向著AC自動機前進吧!希望你喜歡這篇BLOG!
字典樹練習題:
HDU1251(本題原版)
HDU1075
HDU1800