学习笔记:字典树
字典树
引入
字典树(又称 Trie 树)是一种树形数据结构,可以用来存储和查询字符串。例如,,,,, 这几个字符串可以用这样一棵字典树存储:
实现
Trie 树每一个字符占据树上的一个节点,拥有相同前缀的字符串可以共用部分节点。我们令起始点为特殊点(不存储字符),所有字符串均从深度为 (起始点深度为 )的节点开始存储。对于每一个节点,我们都打上标记:
for(int i = 1 ; i <= n ; i ++){
cin >> s + 1;root = 1; // 读入一个将要插入 Trie 树的字符串,并将指针初始化到起始点
for(int j = 1 ; s[j] != 0 ; j ++){ // 将字符串从头到尾扫一遍
ch = s[j] - 'a'; // 计算字符对应的 ASCII 码
if(trie[root][ch] == 0) // 若该节点未加入 Trie 树
trie[root][ch] = ++tot; // 将节点加入 Trie 树
root = trie[root][ch]; // 将指针指向下一节点
}
tag[root] = 1; // 标记该字符串存在
}
不难看出,Trie 树的具体实现是一个二维数组,插入字符串时将对应节点打上标记。
至于查询的话,依然是对着要查询的字符串从头到尾扫一遍,边扫边在 Trie 树上匹配。
for(int i = 1 ; i <= m ; i ++){
cin >> s + 1;root = 1; // 读入要查询的字符串,并将指针初始化到起始点
for(int j = 1 ; s[j] != 0 ; j ++){ // 将字符串从头到尾扫一遍
ch = s[j] - 'a'; // 计算字符对应的 ASCII 码
root = trie[root][ch]; // 将指针指向下一节点
if(root == 0)break; // 若该节点为字节点,则直接停止匹配,否则继续
}
if(tag[root] == 1){ // 若字符串存在且仅插入一次
cout << "OK" << endl;tag[root] = 2;
}else if(tag[root] == 2)cout << "REPEAT" << endl; // 若字符串存在且插入多次
else cout << "WRONG" << endl; // 若字符串不存在
}
在查询时,通常采取边扫边匹配的做法,匹配完之后再查询标记数组所记录的插入次数。
字典树是一种空间换时间的数据结构,我们牺牲了 的空间,但可以用 的时间查询,其中 为查询的前缀或字符串的长度。
看题看题:
于是他错误的点名开始了
简要题意
给定 个字符串,有 次询问。对于每一个询问存在三种情况:
- 若询问的字符串出现过一次,输出
OK
。 - 若询问的字符串从未出现过,输出
WRONG
。 - 若询问的字符串出现过多次,输出
REPEAT
。
思路
Trie 树板子题。
直接将前 个字符串加入 Trie 树中再分情况处理每一个询问即可。
#include <iostream>
#define MAXN 10005
#define MAXM 100005
#define MAXL 55
using namespace std;
int n, m, tot = 1, root, ch;
char s[MAXL];
int trie[MAXM << 2][26], tag[MAXM << 2]; // 记得开四倍空间
int main(){
ios::sync_with_stdio(false);
cin >> n;
for(int i = 1 ; i <= n ; i ++){
cin >> s + 1;root = 1; // 读入一个将要插入 Trie 树的字符串,并将指针初始化到起始点
for(int j = 1 ; s[j] != 0 ; j ++){ // 将字符串从头到尾扫一遍
ch = s[j] - 'a'; // 计算字符对应的 ASCII 码
if(trie[root][ch] == 0) // 若该节点未加入 Trie 树
trie[root][ch] = ++tot; // 将节点加入 Trie 树
root = trie[root][ch]; // 将指针指向下一节点
}
tag[root] = 1; // 标记该字符串存在
}
cin >> m;
for(int i = 1 ; i <= m ; i ++){
cin >> s + 1;root = 1; // 读入要查询的字符串,并将指针初始化到起始点
for(int j = 1 ; s[j] != 0 ; j ++){ // 将字符串从头到尾扫一遍
ch = s[j] - 'a'; // 计算字符对应的 ASCII 码
root = trie[root][ch]; // 将指针指向下一节点
if(root == 0)break; // 若该节点为字节点,则直接停止匹配,否则继续
}
if(tag[root] == 1){ // 若字符串存在且仅插入一次
cout << "OK" << endl;tag[root] = 2;
}else if(tag[root] == 2)cout << "REPEAT" << endl; // 若字符串存在且插入多次
else cout << "WRONG" << endl; // 若字符串不存在
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】