字典樹
概述
字典樹,又稱單詞查找樹,Trie樹,是一種樹形結構,是一種哈希樹的變種。典型應用是用於統計,排序和保存大量的字符串(但不僅限於字符串),所以經常被搜索引擎系統用於文本詞頻統計。它的優點是:利用字符串的公共前綴來減少查詢時間,最大限度地減少無謂的字符串比較,查詢效率比哈希樹高。(引自百度百科《字典樹》)
光說不懂,上引例——
NKOJ 1934 外地人
你考入大城市沙坪壩的學校, 但是沙坪壩的當地人說着一種很難懂的方言, 你完全
聽不懂。 幸好你手中有本字典可以幫你。 現在你有若干個聽不懂的方言需要查詢字典。
輸入格式:
第一行,兩個整數n和m。
接下來有n行表示字典的內容,每行表示一條字典的記錄。每條記錄包含兩個空格間隔的單詞,第一個單詞為英文單詞,第二個單詞為對應的沙坪壩方言。
接下來有m行,每行一個單詞,表示你要查詢的沙坪壩方言。
輸出格式:
輸出m行,每行一個英文單詞,表示翻譯后的結果。
如果某個單詞字典查不到,輸出"eh"
樣例輸入:
5 3
dog ogday
cat atcay
pig igpay
froot ootfray
loops oopslay
atcay
ittenkay
oopslay
樣例輸出:
cat
eh
loops
注:所有單詞都用小寫字母表示, 且長度不超過10。
傳送門:http://oi.nks.edu.cn/zh/Problem/Details/1934
我們看一下這張圖先感受一下Trie樹的結構,它是首先建立一個Root根節點,然后在讀取后來的字符串的同時,從根節點出發,查找字符串每一位的節點是否存在。若存在,就從這一位出發繼續查找下一位;若不存在,就建立這個節點。反復以上過程。注意,Trie樹是將字符轉換為ASCLL碼存取,注意轉換。
顯然,借用這樣的數據結構,我們可以方便存取大量字符串,大幅度優化空間復雜度。
(不知道ASCLL的點這里)
Trie Tree的特點
1. 根節點不包含字符, 除根節點外每一個節點都只包含一個字符。
2. 從根節點到某一節點, 路徑上經過的字符連接起來, 為該節點對應的字符串。
3. 在trie樹中查找一個關鍵字的時間和樹中包含的結點數無關, 而取決於組成關鍵字的字符數。 也就是查找字符串s的時間為O(s.length())
4. 如果要查找的關鍵字可以分解成字符序列且不是很長, 利用Trie樹查找速度優於二叉查找樹。
如:若關鍵字長度最大是5, 則利用Trie樹, 利用5次比較可以從265=11881376個可能的關鍵字中檢索出指定的關鍵字。 而利用二叉查找樹至少要進行log2265=23.5次比較。
接下來先給出引例題解的main函數部分(部分初始化未給出)——
struct node {
int Num; //如果該節點是一個單詞的結尾,記錄對應單詞的編號
int Next[26]; //兒子節點的編號
}trie[1000001];
string s[100001], a;
int main() {
cin >> n >> m;
for (k = 1; k <= n; k ++){
cin >> s[k] >> a;
Insert(a, k);
}
for (k = 1; k <= m; k ++) {
cin >> a;
ans = Find(a);
if (ans)cout << s[ans];
else cout << "eh" << endl;
}
return 0;
}
接着是兩個函數的部分——
void Insert(string c, int k) {
int i, t, len, p = 1;
len = c.length();
for (i = 0; i < len; i ++) {
t = c[i] - 'a';//將字符c[i]轉換成值為0到25的數字,比如'a'轉換為0,'b'轉換為1,‘c’轉換為2……
if (trie[p].Next[t] == 0) { //若p沒有值為t的兒子
tot ++; //新增一個編號為tot的節點
trie[p].Next[t] = tot; //記下p的值為t的孩子節點的編號
p = trie[p].Next[t]; //p指向新添加的節點
trie[p].Num = 0; //初始化新添加的節點,將其標記為不是單詞的結尾
} else p = trie[p].Next[t]; //若p存在值為t的兒子,p指向該兒子,繼續討論
}
trie[p].Num = k; //for循環已執行完,說明第k個單詞已加入,在單詞結尾做上標記
}
int Find(string c) {
int i, t, len, p = 1;
len = c.length();
for (i = 0; i < len; i ++) {
t = c[i] - 'a';
if (trie[p].Next[t] == 0)return 0; //當前要匹配值為t的字母,若沒有則結束
p = trie[p].Next[t]; //若存在值為t的字母,則繼續匹配
}
return trie[p].Num; //若for循環執行完畢,說明找到了需要的單詞,返回其編號
}
以上的代碼幾乎就是字典樹的模板,在不同的題中main函數或許有所不同,可以借此熟悉一下字典樹的工作原理,再酌情修改。
Trie樹的應用
(1) 字符串檢索
(2) 字符串最長公共前綴
#######提供幾道字典樹的簡單練習:
NKOJ 1931 電話簿
NKOJ 1932 找出克隆人
NKOJ 1933 彩色木條
NKOJ 1935 圖書管理員
01字典樹
01字典樹和普通的字典樹原理類似,只不過把插入字符改成了插入二進制串的每一位(0或1)。裸的Trie樹可以降低空間復雜度,而01還可以降低時間復雜度。
它與普通的字典樹一樣先建立Root根節點,但它不存取復雜字符串,而只能存取含有“0”或“1”字符串或數字串。(所以十進制整數可以看做二進制進行存取)以首位為第一個節點建樹,按照前面講解的普通Trie樹的工作原理,我們可以得到一個二叉樹,而深度由數字范圍決定,比如深度為20的01字典樹可以進行存取0~221-1的所有數。
后綴自動機
AC自動機
(后續補充)