赞
踩
Trie树,也叫字典树,前缀树。是一种用于实现字符串快速检索
的多叉树
数据结构。它支持两种操作:
在树中插入一个字符串
字符串检索
Trie树的每个节点都拥有若干个字符指针,它可以用来插入字符串或者进行字符串检索。
初始化
Trie树刚开始时只有一个根节点,它的所有指针均指向NULL
。
根节点并不存储信息,只作为树的标识,进行字符串插入和检索时从根节点进入并扫描其节点,原因也容易理解,根节点只有一个,所以只能存储一个字符,如果要在根节点存入字符的话,就需要开多棵Trie树分别存入不同的开头字母,这显然有些多此一举了。
插入
假设我们此时需要插入一个字符串s
,起初我们令一个指针p
指向根节点,依次扫描s
的每个字符c
,此时我们会发现有两种情况:
p
的c
字符指针指向了一个已存在的节点q
,则p
直接走到q
,然后更新p
,即p = q
p
的c
指针指向空,此时我们新建一个节点q
,然后将p
走到q
,然后更新p
,即p = q
当字符串s
扫描完毕时,在当前节点打上标记。因为如果不打上标记的话,假如此时存入了一个字符串
b
a
t
m
a
n
batman
batman,然后我们要查询是否有存入过
b
a
t
bat
bat,没有标记的话,就无法判断是否插入过
b
a
t
bat
bat了。
检索
检索和插入很像。
假设我们此时需要检索一个字符串s
,起初我们令一个指针p
指向根节点,依次扫描s
的每个字符c
,此时我们会发现有两种情况:
p
的c
字符指针指向了一个已存在的节点q
,则p
直接走到q
,然后更新p
,即p = q
p
的c
指针指向空,则代表该字符串没有插入过,此时我们结束检索当s
中的字符扫描完毕时,若当前p有被标记过,则说明s
在Trie中存在,否则不存在。
假设此时我们需要插入一个字符串cat
创建指针p
进入根节点,扫描其子节点,发现并没有c
这个节点,因此创建一个c
节点,然后将p
移动到c
p
扫描c
的子节点,发现并没有a
字符,创建并移动到a
p
扫描a
的子节点,发现并没有t
,创建并移动到t
此时我们发现字符串已经扫描完毕,标记p
假设我们此时还需要插入一个字符串cab
创建指针p
进入根节点,扫描其子节点有没有字符c
,发现是有的,直接进入c
扫描p
的子节点有没有a
,发现是有的,直接进入a
扫描p
的子节点,看看有没有b
,发现并没有,创建并将p
移动到节点b
此时发现字符串cab
已扫描完毕,标记p
其余也是类似,此处就不再过多说明。
维护一个字符串集合,支持两种操作:
I x
:向集合中插入一个字符串 x
;
Q x
:询问一个字符串在集合中出现了多少次。
共有N
个操作,输入的字符串总长度不超过
1
0
5
10^5
105,字符串仅包含小写英文字母。
第一行包含整数N
,表示操作数。
接下来N
行,每行包含一个操作指令,指令为I x
或Q x
中的一种。
对于每个询问指令Q x
,都要输出一个整数作为结果,表示x
在集合中出现的次数。
每个结果占一行。
1 ≤ N ≤ 2 ∗ 1 0 4 1≤N≤2∗10^4 1≤N≤2∗104
5
I abc
Q abc
Q ab
I ab
Q ab
1
0
1
此题只有插入和查询字符串次数两个操作,那么用哈希表显然是一种快准狠的方法,在此就不多赘述了,读者感兴趣可以自己写写
#include<iostream> #include<unordered_map> #include<cstring> using namespace std; int n; unordered_map<string,int> mp; int main(){ cin >> n; while(n --){ char op[2]; string str; cin >> op >> str; if(op[0] == 'I') mp[str] ++; else printf("%d\n",mp[str]); } return 0; }
使用Trie树,此时我们会发现询问操作是询问我们字符串出现次数,那么插入结束时的标记操作就可以转换为计数操作(插入结束时在尾部 + 1),其余并没有什么变化。
#include<iostream> #include<cstring> using namespace std; const int N = 100010; int n; char str[N]; int son[N][26],cnt[N],idx; void insert(char str[]){ // 插入字符串str int p = 0; // 创建p指向根节点 for(int i = 0; str[i]; i ++){ // 遍历待插入字符串 int u = str[i] - 'a'; // 将 a ~ z 映射为 0 ~ 25 if(!son[p][u]) son[p][u] = ++ idx; // 如果p没有u这个子节点,则创建出该子节点 p = son[p][u]; // 将p移动到该子节点中 } cnt[p] ++; // 表示该字符串出现次数 + 1 } int query(char str[]){ // 检索 int p = 0; // 创建指针p指向根节点 for(int i = 0; str[i]; i ++){ // 遍历待检索字符串 int u = str[i] - 'a'; // 将 a ~ z 映射为 0 ~ 25 if(!son[p][u]) return 0; // 如果p没有u这个子节点,说明没有该字符串,返回0 p = son[p][u]; // 将p移动到该子节点中 } return cnt[p]; // 返回字符串出现的次数 } int main(){ 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; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。