字典樹基礎進階全掌握(Trie樹、01字典樹、后綴自動機、AC自動機)


字典樹

概述

    字典樹,又稱單詞查找樹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自動機

(后續補充)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM