「Day-2 提高笔记-字典树」
字典树
字典树是什么?
理论知识
- 插入操作
我们在插入的时候,先从根节点去向下遍历。对于字符串 \(S\) 的一位 \(S_i\)。
- 如果发现其在字典树中当前节点下有这个字符 \(S_i\),则继续向下,在向下的过程中每次给当前节点的次数加 \(1\),记录字符串前缀数量。
- 若无这个字符,则开辟一个新的节点,记录节点编号,继续向下。
- 查询前缀数量
我们在查询前缀的时候,先从根节点去向下遍历。对于字符串 \(S\) 的一位 \(S_i\)。
- 若在字典树当前节点下没有 \(S_i\),则证明没有以该前缀为开头的单词。
- 否则就继续向下,直到 \(S_n\),最后返回该节点数下的前缀数量。
时间复杂度
对于一个长度为 \(n\) 字符串。
\(Insert\) 和 \(query\) 的时间复杂度都是 \(O(n)\)。
代码实现
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3e6 + 5;
int T;
int n,q;
char s[MAXN];
int ch[MAXN][100],cnt[MAXN],id = 0;
int got(char c){
if(c >= 'A' && c <= 'Z') return c - 'A';
if(c >= 'a' && c <= 'z') return c - 'a' + 26;
return c - '0' + 52;
}
void Insert(char *s){
int now = 0;
for(int i = 0;s[i];i ++){
int v = got(s[i]);
if(!ch[now][v]){
ch[now][v] = ++ id;
}
now = ch[now][v];
cnt[now] ++;
}
return;
}
int query(char *s){
int now = 0;
for(int i = 0;s[i];i ++){
int v = got(s[i]);
if(!ch[now][v]) return 0;
now = ch[now][v];
}
return cnt[now];
}
void Clear(){
memset(ch, 0, sizeof(ch[0]) * (id + 1));
memset(cnt, 0, sizeof(cnt[0]) * (id + 1));
id = 0;
}
signed main(){
scanf("%d",&T);
while(T --){
scanf("%d%d",&n,&q);
for(int i = 1;i <= n;i ++){
scanf("%s",s);
Insert(s);
}
for(int i = 1;i <= q;i ++){
scanf("%s",s);
cout << query(s) << '\n';
}
Clear();
}
return 0;
}
例题
P1481 魔族密码
这个题最开始我没想到怎么写,后来就发现,我们只要暴力 \(dfs\) 去跑一遍这个树即可,在遍历的过程中记录这条路径上的节点有多少个是单词的结尾。我们在 \(Insert\) 里面加一个判断节点 \(p\) 是否是单词结尾的 \(bool\) 变量即可。
#include<iostream>
using namespace std;
const int MAXN = 3 * 1e4 + 5;
int ans = 0;
int n,q;
char s[MAXN];
bool isend[MAXN];
int ch[MAXN][100],cnt[MAXN],id = 0;
void Insert(char *s){
int now = 0;
for(int i = 0;s[i];i ++){
int v = s[i] - 'a';
if(!ch[now][v]){
ch[now][v] = ++ id;
}
now = ch[now][v];
cnt[now] ++;
}
isend[now] = 1;
return;
}
void dfs(int p,int step){
step += isend[p];
bool back = 1;
for(int i = 0;i < 26;i ++){
if(ch[p][i] != 0){
dfs(ch[p][i],step);
back = 0;
}
}
if(back == true){
ans = max(ans,step);
}
}
signed main(){
scanf("%d",&n);
for(int i = 1;i <= n;i ++){
scanf("%s",s);
Insert(s);
}
dfs(0,0);
cout << ans << '\n';
return 0;
}
本文来自一名初中牲,作者:To_Carpe_Diem