介绍
在计算机科学中,trie,又称前缀树或字典树,是一种有序树,用于保存关联数组,其中的键通常是字符串。与二叉查找树不同,键不是直接保存在节点中,而是由节点在树中的位置决定。一个节点的所有子孙都有相同的前缀,也就是这个节点对应的字符串,而根节点对应空字符串。一般情况下,不是所有的节点都有对应的值,只有叶子节点和部分内部节点所对应的键才有相关的值。
trie中的键通常是字符串,但也可以是其它的结构。trie的算法可以很容易地修改为处理其它结构的有序序列,比如一串数字或者形状的排列。比如,bitwise trie中的键是一串位元,可以用于表示整数或者内存地址。
基本性质
-
根节点不包含字符,除根节点外,每个节点只包含一个字符。
-
从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串。
-
每个节点的所有子节点包含的字符串不相同。
应用场景:
(1)字符串检索;(2)文本预测、自动完成,see also,拼写检查;(3)词频统计;(4)排序;(5)字符串最长公共前缀;(6)字符串搜索的前缀匹配
参考文章:Trie(前缀树/字典树)及其应用
实战例题
维护一个字符串集合,支持两种操作:
I x 向集合中插入一个字符串 x;
Q x 询问一个字符串在集合中出现了多少次。
共有 N个操作,输入的字符串总长度不超过 105,字符串仅包含小写英文字母。
输入格式
第一行包含整数 N,表示操作数。接下来 N行,每行包含一个操作指令,指令为 I x 或 Q x 中的一种。
输出格式
对于每个询问指令 Q x,都要输出一个整数作为结果,表示 x在集合中出现的次数。每个结果占一行。
数据范围
1≤N≤2∗104
输入样例:
5
I abc
Q abc
Q ab
I ab
Q ab
输出样例:
1
0
1
算法实现
#include <stdio.h>
const int N = 1e5 + 10;
// N个结点,26个空间中对应26个不同的英文字母,每个空间中对应所连子结点的下标
int son[N][26];
// cnt[i]记录,当到达下标为i的结点时,已被标记的单词数量(不是字母数量)
int cnt[N];
// 记录当前存储空间可用于存储结点的下标
int idx;
void insert(char str[]){
int p = 0;
for(int i = 0; str[i] != '\0'; i++){
int u = str[i] - 'a'; // 归一化为0~25的数字来表示a~z
if(!son[p][u]) son[p][u] = ++idx; // 下标为0的点为根节点
p = son[p][u];
}
cnt[p]++;
}
int query(char str[]){
int p = 0;
for(int i = 0; str[i]; i++){
int u = str[i] - 'a';
if(!son[p][u]) return 0;
p = son[p][u];
}
return cnt[p];
}
int main(){
int n; scanf("%d", &n);
char str[N];
while(n--){
// 避免读入时读取空格,因此设置为字符串形式
char op[2]; scanf("%s%s", &op, &str);
if(op[0] == 'I') insert(str);
else printf("%d\n", query(str));
}
return 0;
}
当所有字符串长度之和为n,构建字典树的时间复杂度为O(n)
但要查找的字符串长度为k,查找的时间复杂度为O(k)