这里是paoxiaomo,一个现役ACMer,之后将会持续更新算法笔记系列以及笔试题题解系列
本文章面向想打ICPC/蓝桥杯/天梯赛等程序设计竞赛,以及各个大厂笔试的选手
感谢大家的订阅➕ 和 喜欢💗
这次的蓝桥杯浙江省省赛出现了字典树的题(详情【2024浙江省蓝桥杯C++B组省赛】题解A-D(含题面)-CSDN博客),因此这次我们来详细讲解一下字典树。
字典树通常用于实现词典或者前缀搜索。它的每个节点代表一个字符,从根节点到每个节点的路径构成一个单词。字典树常常用于统计,保存大量字符串并且相比于别的字符串算法,它的统计只和正在被查询的字符串长度有关,在不少问题上优于哈希等其它字符串算法。
先说一下基本操作:
-
插入操作: 将一个新的单词插入字典树中。从根节点开始,沿着单词的每个字符向下遍历,如果某个字符对应的子节点不存在,则创建新的子节点;最后标记最后一个字符节点为单词结束。
-
查找操作: 在字典树中查找一个单词。从根节点开始,沿着单词的每个字符向下遍历,如果某个字符对应的子节点不存在,则说明字典树中不存在该单词;如果遍历完所有字符后,最后一个字符节点标记了单词结束,则说明该单词存在于字典树中。
-
前缀搜索: 找到所有以某个给定前缀开头的单词。从根节点开始,沿着前缀的每个字符向下遍历,直到到达前缀的最后一个字符节点,然后通过DFS(深度优先搜索)遍历以该节点为根的子树,收集所有以该前缀开头的单词。
如何实现?
我们用tr[N][26]表示维护每个节点的子节点,num维护每个节点被经过的次数,tot代表节点总数,其中0是初始节点。即可实现插入/查询前缀数之和.
int tr[N][26], tot, num[N];
void inset(string s){
int now = 0;
for (int j = 0; j < s[i].size(); j++)
{
if (!tr[now][s[i][j] - 'a'])
{
tr[now][s[i][j] - 'a'] = ++tot;//找不到就新开节点
}
now = tr[now][s[i][j] - 'a'];
num[now]++;
}
}
int find(string s){
int now = 0;
for (int j = 0; j < s[i].size(); j++)
{
if (!tr[now][s[i][j] - 'a'])
{
break;//找不到相应节点就退出
}
now = tr[now][s[i][j] - 'a'];
sum += num[now];
}return sum
}
如图,以上字典树可以代表着字符串集:aa,aba,ba,cc,caaa,cab,cba.可以尝试模拟一下建树过程加深理解。
除了表示字符之外,节点可能还存储其他信息,如该节点对应的字符是否是一个单词的结束,或者该单词在词典中出现的频率等。同时,字典树也是后缀自动机等高级字符串处理算法的前置知识。
关注博主,带你学习更多竞赛/笔试实用算法