trie 樹,又叫字典樹,前綴樹,能夠用來求查詢這個字符串的數量,或者以這個字符串爲前綴的字符串的數量。ios
trie 樹用了一個前綴的思想,就是把每一個字符串拆一位位拆開來,而後存在樹的邊上,好比有 cup,apple,cake,app,blog 這幾個單詞,那麼咱們就能夠構建這樣一棵有根樹:
可是,在樹上邊是很很差維護的,因而咱們能夠把他放在這條邊的兩個端點中,深度較大的那個點上,像這樣:
但這樣還沒完,咱們如何判斷這是一個完整字符串呢?好比,你如何知道 ca 是否是一個完整的字符串呢?是判斷最後一個字符是否爲葉子節點嗎?明顯不是,上面的例子中,app的最後一個字符就不是葉子節點。那怎麼辦呢?直接給節點打個標記嘛~
因而,咱們知道了什麼是 trie 樹,接下來咱們要解決插入和查詢的問題。
咱們能夠開一個數組,用 \(trie_{i,j}\) 表示節點 i 的兒子中,是字符 j 的節點的編號。固然,咱們不能用字符作下標,可是咱們能夠把他轉成數字,好比題目說明只會出現 a~z 的字符,那麼咱們能夠用 x-'a'
來表示字符 x。
而後,對於插入和查詢操做,咱們只要枚舉字符串的每一位,而後用一個變量 rt
來記錄當前節點的編號。而後,對於第 i 個字符,咱們只要查詢它是否存在,也就是 \(trie_{rt,s[i-1]}\) 是否爲 \(0\)(字符串下標從 \(0\) 開始),若是爲 \(0\),如果插入,則加入這個節點;如果查詢,則返回 false。
代碼以下:數組
void insert(string s) { int len=s.length(),root=0;//root即上面的rt for(int i=0;i<len;i++) { int id=s[i]-'a'; if(trie[root][id]==0) trie[root][id]=++tot;//tot記錄節點數 root=trie[root][id]; } v[root]=true;//v就是用來記錄是否爲字符串結尾的、 return ; } bool find(string s) { int len=s.length(),root=0; for(int i=0;i<len;i++) { int id=s[i]-'a'; if(trie[root][id]==0) return false; root=trie[root][id]; } if(v[root]) return true; else return false; }
而後,上面說到咱們能夠查詢以這個字符串爲前綴的字符串有多少,怎麼弄呢?咱們能夠用 \(sum_i\) 表示有從根節點到 \(i\) 這個字符串(trie 樹上從根節點到一個節點的路徑其實就是一個字符串的前綴)前綴的字符串的數量(有點繞、),而後每次插入走到一個節點就 sum[i]++
便可,而查詢則和上面的同樣,最後返回末尾節點的 sum 便可。
代碼實現以下:app
int fin_pre(string s) { int len=s.length(),root=0; for(int i=0;i<len;i++) { int id=s[i]-'a'; if(trie[root][id]==0) return 0; root=trie[root][id]; } return sum[root]; }
總體代碼實現以下:函數
#include<cstdio> #include<string> #include<iostream> using namespace std; int n,m; int tot,trie[1000005][26],sum[1000005]; bool v[1000005]; void insert(string s) { int len=s.length(),root=0; for(int i=0;i<len;i++) { int id=s[i]-'a'; if(trie[root][id]==0) trie[root][id]=++tot; root=trie[root][id]; sum[root]++; } v[root]=true; return ; } bool find(string s) { int len=s.length(),root=0; for(int i=0;i<len;i++) { int id=s[i]-'a'; if(trie[root][id]==0) return false; root=trie[root][id]; } if(v[root]) return true; else return false; } int find_pre(string s) { int len=s.length(),root=0; for(int i=0;i<len;i++) { int id=s[i]-'a'; if(trie[root][id]==0) return 0; root=trie[root][id]; } return sum[root]; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { string s; cin>>s; insert(s); } for(int i=1;i<=m;i++) { int opt; scanf("%d",&opt);//opt=1表示查詢這個字符串是否存在,opt=2表示查詢包含這個字符串前綴的字符串的數量 string s; cin>>s; if(opt==1) printf("%s\n",find(s)?"Yes":"No"); else printf("%d\n",find_pre(s)); } return 0; }
因爲沒有找到合適的模板題因此沒法測驗,但 insert 和 find 函數是沒問題的,find_pre 也不會出太大的問題spa