trie树
trie字典树目录
本文用于整理 ACM trie树论相关知识点与题目,方便自己和队友查阅。
持续更新中……
一、Trie 树是什么
Trie 树,又叫字典树、前缀树,是一种用于高效存储和查找字符串集合的多叉树结构。
它的核心思想是:把字符串的公共前缀合并到同一条路径上,从而减少存储空间、加快查询速度。
例如插入 cat、car、card、dog 后,Trie 树长这样:
root
/ \
c d
| |
a o
| |
t/r g
\
d
可以看到 cat、car、card 共享了前缀 ca,节省了空间。
二、适用场景
- 字符串集合的插入与查询
- 前缀查询:判断是否存在以某串为前缀的字符串
- 异或最大值:01-Trie(把数字按二进制插入)
- 字符串排序:按字典序输出
- AC 自动机:Trie + KMP 思想
- 单词统计:统计某字符串出现次数
三、核心操作
1. 插入字符串
从根节点出发,逐字符向下走:
- 如果对应子节点不存在,就新建一个
- 如果存在,就复用
- 走到最后一个字符时,在末尾节点打上标记(表示这是一个完整单词)
2. 查询字符串
从根节点出发,逐字符向下走:
- 如果中途某个字符对应的子节点不存在 → 字符串不在集合中
- 如果能走到最后,并且末尾节点有标记 → 字符串存在
- 如果能走到最后,但末尾节点没有标记 → 只是前缀,不是完整单词
3. 查询前缀
和查询字符串类似,但不需要检查末尾标记,只要能走完整个前缀即可。
四、模板代码
以「小写字母字符串」为例,字符集大小 26。
数组版(推荐,速度快)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5; // 最大节点数
const int M = 26; // 字符集大小
int trie[N][M]; // trie[p][c] 表示节点 p 走字符 c 到达的节点编号
int cnt[N]; // cnt[p] 表示以节点 p 结尾的单词数量
int tot = 0; // 当前节点总数,根节点编号为 0
// 插入字符串 s
void insert(const string &s)
{
int p = 0; // 从根节点出发
for (char ch : s)
{
int c = ch - 'a';
if (!trie[p][c]) // 子节点不存在
trie[p][c] = ++tot; // 新建节点
p = trie[p][c]; // 向下走
}
cnt[p]++; // 末尾节点计数 +1
}
// 查询字符串 s 出现的次数
int query(const string &s)
{
int p = 0;
for (char ch : s)
{
int c = ch - 'a';
if (!trie[p][c]) return 0; // 走不下去,不存在
p = trie[p][c];
}
return cnt[p];
}
// 查询是否存在以 s 为前缀的字符串
bool queryPrefix(const string &s)
{
int p = 0;
for (char ch : s)
{
int c = ch - 'a';
if (!trie[p][c]) return false;
p = trie[p][c];
}
return true;
}
六、经典例题
1. 前缀匹配
维护一个字符串集合,支持插入字符串、查询某字符串出现次数。
- 思路: 给定n个字符串,找含前缀的模板串的文本串数量
- 做法:依次加入trie树中,维护维护一个字符串集合,并更新含有该字符的数量
2. 01字典树
-- The XOR Largest Pair(01-Trie 经典题)
-
思路: 给定 n 个数,求任意两数异或的最大值。
-
做法:把每个数按二进制(从高位到低位)插入 01-Trie,查询时贪心走相反位。
- 思路:给定 n 个数,求任意两数异或的最大值。
- 做法:把每个数插入后进行维护trie数,全局加一:从根开始,交换左右子树(第 b 位翻转),对新的左子树递归处理进位。
3. 字符串统计(未完成)
七、总结
| 要点 | 内容 |
|---|---|
| 本质 | 用公共前缀合并路径的多叉树 |
| 核心操作 | 插入、查询、前缀查询 |
| 时间复杂度 | 每次操作 O(L) |
| 典型应用 | 字符串统计、前缀匹配、01-Trie 异或最大值 |
| 常见变体 | 01-Trie、可持久化 Trie、AC 自动机 |

浙公网安备 33010602011771号