字典樹 Trie樹 和幾道題目

2021-09-25 15:19:25 字數 4145 閱讀 8897

補了補昨天rdc學長講的關於字串的知識點。字典樹就是在樹上存了一些單詞(字串),可以用來查詢某個單詞在其中是否存在,或者查詢樹上所有單詞和某個字串的公共字首,問題形式也不算很多,學長說這主要是作為ac自動機的組成部分來考察。

樹的樹根為一空節點,從樹根開始遍歷一條路就能得到乙個單詞,而建樹時是基於某些字串的公共字首來優化建樹空間和時間的。下面是建樹**,trie[i][id]表示字母在樹上i位置後的字母id儲存的位置,cnt用來統計樹上的節點數。

void

insert

(string s)

h=trie[h]

[id]

;//繼續沿此路向後建樹,直到存下整個單詞。這裡可能會寫錯,切記不能寫在if裡面,因為字母id無論是否存在,都要向後繼續走

}return

;}

查詢操作的**與上面大致相似,沿樹上某條路走到葉子節點後如果還未跑完整個待查詢單詞,說明樹上沒有這個單詞,否則查詢成功。

bool query

(string s)

else

}return1;

}

這樣字典樹的建樹和查詢的模板就碼完了,看題:

luogup2580

這應該才是真正的板子題(難度等級也太低了吧),給一些名字,問後面每次點的名是否存在 \ 是否點重複了。

建樹,按名字匹配,匹配成功時記得存一下樹上這個位置已經到達過了,以後再匹配成功時就可以判斷是否重複。

(改一下上面的板子就夠了,不貼碼了)

hdu1251

先輸入一些單詞組成字典,之後再給一些字首,問每個字首在字典**現的次數。

簡單分析:建樹時,若單詞有公共字首會重複走到一些點,可以開乙個新陣列,sum[u]表示建樹時重複走到節點u的次數,查詢的時候在樹上跑完每個字首,直接輸出其終點的sum就ok了。

#include 

#include

#include

#include

#include

#include

#include

#define ll long

long

using namespace std;

const

int maxn=

500000+5

;int trie[maxn][26

];int cnt;

int sum[maxn]

;void

insert

(string s)

h=trie[h]

[id]

; sum[h]++;

}return;}

intquery

(string s)

else

}return sum[h];}

intmain()

string p;

while

(getline

(cin,p)

)return0;

}

[usaco08dec]秘密訊息secret message

和上一題類似,給一些已知字首,再給一些新字串,對每個新字串,問給出的字首中可能與其匹配的有多少。

題意有點繞,要注意可能存在新字串比字首還要短的情況(亂亂亂),而且這種情況下只要新字串的能匹配成功還是要計入答案的!

引入新陣列endh,endh[i]表示在字典樹上位置i結束的單詞有多少個,只需要在建樹函式中插入單詞結束時更新一下就可以了。

然後分別考慮:ⅰ,樹上的存的單詞比當前查詢的字串短,回頭看一下query函式,當查詢到失配位置時退出,

答案為每個匹配成功位置的endh累加(因為要求的是樹上滿足條件的單詞個數)而且這樣是不會重複統計某個答案的。

ⅱ,樹上存的單詞長度大於等於當前查詢的字串,一旦某個位置失配,其實和情況ⅰ中失配情況一樣,輸出到當前失配位置前累加的endh。當跑完當前查詢的字串時,有可能在樹的某條路上還沒走到重點,所以這時要再加上我們上題中提到的sum[u]。

很合理,提交,wa了。

到這裡忽略了乙個細節,回憶一下那兩個陣列的意義,sum[u]記錄經過這個點的有多少單詞,endh[u]記錄以這個點為終點的有多少單詞,而經過這個點的單詞一定包括以這個點為終點的單詞,所以此時答案要減去endh[u]。陣列維護時出錯了就沒辦法了

#include 

#include

#include

#include

#include

#include

#include

#define ll long

long

using namespace std;

const

int maxn=

500000+5

;int trie[maxn][2

];int cnt;

int n,m,k;

int sum[maxn]

,endh[maxn]

;int b[maxn]

;int

getnum()

while

(isdigit

(c))

return f ? ans :

-ans;

}void

insert

(int num)

h=trie[h]

[id]

; sum[h]++;

} endh[h]++;

return;}

intquery

(int num)

h=trie[h]

[id]

; ans+=endh[h];}

ans=ans+sum[h]

-endh[h]

;return ans;

}int

main()

insert

(b);

}for

(int i=

1;i<=m;i++

)printf

("%d\n"

,query

(b));}

return0;

}

(補個題,更新於2019.7.29)

洛谷p2292

n個字串組成字典,m個模式串,詢問每個模式串的字首能被理解的長度,能被理解的字首指這個字首是否是由若干個字典中的單詞組成,即符合要求的字首是從模式串開頭到最後乙個完整單詞的末尾位置。

注意我們在字典樹上查詢字首的時候會查完乙個單詞就直接退出,因為這個單詞的結尾沒有連線下乙個單詞的開頭,但我們可能要處理若干個單詞。

解決的辦法是預先存好每個單詞結束位置在樹上的節點編號,之後查詢時每跑完乙個單詞,ans更新為當前在模式串上走過的長度,之後再處理後面的子串,看**是下乙個單詞的結束位置。

有點囉嗦了,上碼

#include 

#include

#include

#include

#include

#include

#define ll long

long

const

int maxn=

1000000+5

;using namespace std;

int trie[maxn][26

];int cnt;

int n,m;

bool flag,isend[maxn]

,isendinp[maxn]

;string s,p;

void

gettrie

(string ts)

h=trie[h]

[id];}

isend[h]=1

;return;}

intsearch

(string tp)

for(

int i=

0;ireturn ans;

}int

main()

for(

int j=

1;j<=m;j++

)return0;

}

今天還是學到不少東西的,過兩天再寫樹剖的筆記omo⬅背叛的眼神

Trie樹(字典樹)

trie樹的核心思想是用空間換時間,通過在樹中儲存字串的公共字首,來達到加速檢索的目的。例如,對於一棵儲存由英文本母組成的字串的trie樹,如下圖 trie樹在實現的時候,可以用左兒子右兄弟的表示方法,也可以在每個節點處開設乙個陣列,如上圖的方法。trie樹的主要操作是插入 查詢,也可以進行刪除。插...

字典樹 Trie樹

字典樹 trie樹 顧名思義是一種樹形結構,屬於雜湊樹的一種。應用於統計 排序 查詢單詞 統計單詞出現的頻率等。它的優點是 利用字串的公共字首來節約儲存空間,最大限度地減少無謂的字串比較,查詢效率比雜湊表高。字典樹的結構特點 根節點不代表任何字元。其他節點從當前節點回溯到根節點可以得到它代表的字串。...

字典樹 trie樹

amy 56 ann 15 emma 30 rob 27 roger 52首先存入amy,level 0表示根,不持有資料。其餘每個節點持有乙個字元 葉子節點持有資料,且持有的字元為 0 level 0 root a level 1 m level 2 y level 3 0 56 level 4新...