【数据结构】Trie树
Trie:高效地存储和查找字符串集合的数据结构
Trie树的存储
比如说要存储下面这些字符串
从根结点开始存
然后若一个结点是一个单词的末尾,则要打上一个标记,从根结点到该结点存在一个单词,图中用五角星表示标记。
这是Trie树的存储。
Trie树的查找
Trie树可以高效地查找某个单词是否出现过以及出现了多少次。
比如说要在上图中查找aced
这个字符串,从根结点开始找,找到第三个分支的d
结点,并且d
结点上也有标记,这样我们就找到了这个字符串。
比如说要查找abcf
,先找a
,再找b
,再找c
,此时发现再往下找并没有找到f
,即f
不存在,所以这个字符串也就不存在。
如果继续找abcd
,也是从根结点开始顺着找,我们发现abcd都可以在树中找到,但是,树中d
结点处并没有被打上标记,所以字符串abcd
也是不存在的。
代码模板:
int son[N][26], cnt[N], idx;
char str[N];
void insert(char str[])
{
int p = 0;
for(int i = 0; str[i]; i ++ )
{
int u = str[i] - 'a';
if(!son[p][u]) son[p][u] = ++ idx ;
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];
}
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
/*
下标为0的点即是根结点,也是空节点
cnt[]存的是每个单词出现多少次
*/
int son[N][26], cnt[N], idx;
char str[N];
void insert(char str[]) //插入操作
{
int p = 0;//从根结点开始从前往后遍历
for(int i = 0; str[i]; i ++ ) //C++中字符串结尾是/0,所以可以用str[i]判断是否走到结尾
{
int u = str[i] - 'a'; //把trie树中的字母映射为0~25之间的数字,每次把当前结点字母对应的子节点编号拿出来
//如果当前结点上不存在对应的字母,则把它创建出来(即如果p结点不存在u这个儿子,则创建u)
if(!son[p][u]) son[p][u] = ++ idx;
//此时,当前这个点一定存在,因为不存在我们也已经创建出来了
p = son[p][u]; //走到下一个点
}
//结束的时候p对应的点就是最后一个点
cnt[p] ++ ; //表示以结点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]; //返回以p结尾的单词的数量
}
int main()
{
int n;
scanf("%d", &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;
}