字典树
字典树是什么?
理论知识
- 插入操作
我们在插入的时候,先从根节点去向下遍历。对于字符串 \(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;
}
标签:前缀,int,笔记,Day,MAXN,向下,节点,字典
From: https://www.cnblogs.com/To-Carpe-Diem/p/18471072