字典树(trie) 算法笔记
P1 字典树是什么:
顾名思义就像一个字典一样,可以查询某单词是否出现,也可以查找同一前缀的单词的个数等等操作。
P2 字典树的实现:
字典树是用树来实现的(这不废话吗),如果从根节点走到一个已标记过的节点(后面我们会称它为单词节点)的一条路径就是一个单词。
我们定义一下变量(或数组)的表示含义:
- \(tot\): 表示当前一共有多少个节点。
- \(nex[p][c]\): 表示 \(p\) 节点的走 \(c\) 这条路径的下一个节点。
- \(exist[p]\): 表示 \(p\) 是否为单词节点。
- \(deta[p]\): 表示 \(p\) 节点的属性(权值),这个数组可有可无(通常为经过此节点的次数)。
很快我们就可以写出插入字符串,和查询字符串是否存在的代码,代码如下:
void insert(string s, int p = 0) {
for (int i = 0, c; i < s.size(); i++) {
c = ToId(s[i]); // ToId为字符转整数的函数
!nex[p][c] && (nex[p][c] = ++tot); // 若没有此节点,开辟新节点
p = nex[p][c] /*往下走*/, cnt[p]++; // 此处cnt为上述的deta,为经过此节点的次数
}
exist[p] = 1; // 将此节点标记为但此节点
}
bool query(string s, int p = 0) {
for (int i = 0, c; i < s.size(); i++) {
c = ToId(s[i]);
if (!nex[p][c]) {
return 0;
} // 没有路了没有此单词
p = nex[p][c]; // 往下走
}
return exist[p];
} // query的含义可以改变,此处是查询此字符串是否存在
很容易发现这个数据结构是一个用空间来换时间的数据结构,空间复杂度为:\(O(nmt)\),每次操作的时间复杂度为:\(O(m)\),\(n\) 表示单词的个数,\(m\) 表示单词的长度,\(t\) 表示字符的种类数。
P3 例题解析:
Luogu - P8306 【模板】字典树
题目描述:给你一些 \(n\) 个字符串,\(q\) 此询问,每次询问一个字符串,在前 \(n\) 个字符串种有多少个字符串的前缀是这个字符串。
这道题让你求以某个字符串为前缀的字符串的个数,我们在插入字符串的时候,可以记 \(cnt[p]\) 为为经过此节点的次数,我们发现对于字符串 \(s^{'}\) 以他为前缀的字符串个数,就为 \(cnt[Back(s^{'})]\),(\(Back(s)\) 为 \(s\) 的最后一个字符的节点编号),代码如下: