題目連結
字典樹的介紹
字典樹也叫(Trie樹),字典樹有插入和查詢兩個操作,我們先假設我們已經插入了單詞 be
、fake
、beef
、face
這幾個單詞,那麼我們可以建樹。
當我們查詢 be
、fa
、fAKe
、fac
時,答案分別為:\(2,2,0,1\)。
字典樹的插入
我們可以給樹上的每個節點標號,比如上面的樹,可標為:
根節點永遠都是 \(0\),而其他節點,按訪問順序來標號。
我們定義 son[i][w]
表示現在的編號 i 的下一個字元是 w 的編號是多少,比如,編號 i 所對應的字串是 t[i]
那麼 t[son[i][w]]=t[i]+w
。
設一個變數 p
,表示現在訪問到的節點編號是多少,一開始的時候 p=0
,然後如果 son[p][w]=0
那麼就新建一個節點編號,即 son[p][w]=++idx
。接著 \(p\) 賦值成下一個節點的編號即 son[p][w]
,然後對應字串出現次數 cnt[p]
加一。
程式碼:
int idx=0;
void insert(char *s){
int p=0;
for(int i=0,w;w=s[i];i++){
if(!son[p][w])son[p][w]=++idx;
p=son[p][w];
cnt[p]++;
}
}
字典樹的查詢
首先還是設一個變數 p
表示現在訪問到的節點編號是多少,p
一開始在根節點,然後如果 son[p][w]=0
那麼就表示沒有這種字串,即返回 \(0\),否則的話,我們繼續訪問,最後我們列舉完整個字串,即查詢到 s
字串對應的編號 p
,返回 cnt[p]
即可。
int query(char *s){
int p=0;
for(int i=0,w;w=s[i];i++){
if(!son[p][w])return 0;
p=son[p][w];
}
return cnt[p];
}
模板程式碼
題目連結
就是普通的模板,卡常卡過就行了。
#include<bits/stdc++.h>
#define fast ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);
#define rep(l,r,i) for(int i=l,END##i=r;i<=END##i;i++)
#define per(r,l,i) for(int i=r,END##i=l;i>=END##i;i--)
#define endl '\n'
#define pb push_back
#define mk make_pair
#define pii pair<int,int>
#define vi vector<int>
using ll=long long;
using ull=unsigned long long;
using namespace std;
const int INF=0x3f3f3f3f;
const int MAXN=3e6+10,MAXM=1e5+10;
int T,n,q,cnt[MAXN],idx;
char s[MAXM];
map<char,int>son[MAXN];
void insert(char *s){
int p=0;
for(int i=0,w;w=s[i];i++){
if(!son[p][w]){
son[p][w]=++idx;
cnt[idx]=0;
}
p=son[p][w];
cnt[p]++;
}
}
int query(char *s){
int p=0;
for(int i=0,w;w=s[i];i++){
if(!son[p][w])return 0;
p=son[p][w];
}
return cnt[p];
}
void solve(){
rep(0,idx,i)son[i].clear();
idx=0;
scanf("%d%d",&n,&q);
rep(1,n,i){
scanf("%s",s);
insert(s);
}
while(q--){
scanf("%s",s);
printf("%d\n",query(s));
}
}
int main(){
cin>>T;
while(T--)
solve();
return 0;
}