字典树(Trie)

  • 字典树

例题

【模板】字典树

原理

本质上是存储若干个字符串的公共前缀以减少重复的查询操作,达到提高查询效率的目的。

(本题中的字符串只含数字与大小写字母,所以一共有62个字符)

N 为字符串最大长度。

ch[N][65]ch[p][j]存储从节点 p 沿着值为 j 的这条边走到的子节点,从 'a''z''A''Z''0''9' 一共 62 个字符,对应映射值 061 ,每个节点最多 62 个分叉。

cnt[N]cnt[p] 用来存储节点 p 在插入操作中被访问的次数

idxidx 用于给节点编号。

插入操作

初始阶段仅有一个空节点,编号为 0,不存储任何数据。从根节点开始插入,枚举字符串的每个字符,如果当前节点有子节点是待插入的下一个字符,就走到子节点。如果没有满足的子节点,就先创建子节点,然后走到这个子节点。每次走到子节点的时候,就将这个节点的计数器 +1

查询操作

从根开始查,扫描字符串。如果有字符 s[i],则继续往下走,能走到词尾,则返回访问次数;无字符 s[i],则返回 0.

代码

#include <iostream>
#include <cstring>
using namespace std;
const int N = 3000005;
char str[N];
int ch[N][65], cnt[N], idx;
int getnum(char c)
{//映射字符
if (c >= 'a' && c <= 'z')return c - 'a';
else if (c >= 'A' && c <= 'Z')return c - 'A' + 26;
else if (c >= '0' && c <= '9')return c - '0' + 52;
else return -1;
}
void insert(char s[])
{//插入操作
int p = 0;
for (int i = 0; s[i]; i++)
{
int j = getnum(s[i]);
if (!ch[p][j])ch[p][j] = ++idx;
p = ch[p][j];
cnt[p]++;//将cnt[p]++放在循环内部是因为题目要求查询前缀中含有待查询序列的字符串
}
//如果目的是查询与被查询字符串相等的字符串的个数,就将插入操作改为
/*
int p = 0;
for (int i = 0; s[i]; i++)
{
int j = getnum(s[i]);
if (!ch[p][j])ch[p][j] = ++idx;
p = ch[p][j];
}
cnt[p]++;
*/
}
int query(char s[])
{//查询操作
int p = 0;
for (int i = 0; s[i]; i++)
{
int j = getnum(s[i]);
if (!ch[p][j])return 0;
p = ch[p][j];
}
return cnt[p];
}
void solve()
{
int n, q;
cin >> n >> q;
//清空之前使用过的数组
for (int i = 0; i <= idx; i++)
{
for (int j = 0; j < 65; j++)ch[i][j] = 0;
cnt[i] = 0;
}
idx = 0;
for (int i = 1; i <= n; i++)
{
cin >> str;
insert(str);
}
for (int i = 1; i <= q; i++)
{
cin >> str;
cout << query(str) << endl;
}
}
int main()
{
ios::sync_with_stdio(0);
int T;
cin >> T;
while (T--)
{
solve();
}
return 0;
}
posted @   susenyang  阅读(27)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示