Trie树
定义
又称单词查找树,字典树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。
基本性质
- 根节点不包含字符,除根节点以外每个节点只包含一个字符。
- 从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串。
- 每个节点的所有子节点包含的字符串不相同。
例图
该trie树存放了at,bee,ben,bt,q五个字符串。
该trie存放了abcd,abd,bcd,efg,hij五个字符串。
插入与查询实现方法
插入字典项目的方法为:
(1) 从根结点出发;
(2) 定义关键字第一个字符的值,查找是否存在该子树;
(3) 存在该子树的话就继续查找下一个字符;
(4) 迭代过程……
(5) 最后一个字符插入trie树,标记结束字符(数量加一)。
查询字典项目的方法为:
(1) 从根结点开始一次搜索;
(2) 取得要查找关键词的第一个字母,并根据该字母选择对应的子树并转到该子树继续进行检索;
(3) 在相应的子树上,取得要查找关键词的第二个字母,并进一步选择对应的子树进行检索。
(4) 迭代过程……
(5)如过某节点不存在或不存在下一个字母的子树,说明不存在该关键词。
(6) 如果在某个结点处,关键词的所有字母已被取出,则读取附在该结点上的信息,即完成查找。
题目
题目来源=》AcWing第835题:
维护一个字符串集合,支持两种操作:
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
题目代码(含详细注释)
//trie树
#include <iostream>
using namespace std;
const int N = 100010;
//son[N][26]存放各个字符
//cnt[N]标记该节点是否为最后一位如果是就代表该字符串的数量
//idx表示节点下标,也代表trie数的结点数
int son[N][26], cnt[N], idx;
char str[N]; //存放需要操作的字符串
void insert(char *str)
{ // 字符串插入trie树
int p = 0; //根节点为0
for (int i = 0; str[i]; i ++ )
{
int u = str[i] - 'a'; //当前字母对应的数字(用int型数字代替小写字母)
if (!son[p][u]) son[p][u] = ++ idx; //如果该节点不存在就生成一个节点,此时的p为父节点
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; //如果不存在该节点直接返回 0 ,说明该字符串不存在
p = son[p][u]; // 节点值为子节点的[p]值
}
return cnt[p];
}
int main()
{
int n;
scanf("%d", &n);
while (n -- )
{
char op[2];
scanf("%s%s", op, str);
if (*op == 'I') insert(str);
else printf("%d\n", query(str));
}
return 0;
}
另外哈希表也可以解题:
#include <iostream>
#include <cstring>
#include <unordered_map>
using namespace std;
unordered_map<string,int> mp;
int main(){
int n;
cin >> n;
while(n -- ){
char op;
string x;
cin >> op;
if(op == 'I'){
cin >> x;
mp[x]++;
}
if(op == 'Q'){
cin >> x;
cout << mp[x] << endl;
}
}
return 0;
}
trie树查找时间是O(L)L是字符串长度,而hash是O(LL),LL是关键字对应哈希地址链表长度,都和数据的大小无关,查找都很高效,当时再这题显然tried树的时间复杂度会更低。
结语
(定义啥的百度上找来的,本蒟蒻还不是很理解,希望大佬们多多指教!)