補了補昨天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新...