[AcWing]835. Trie字符串统计(C++实现)前缀树模板题
1. 题目
2. 读题(需要重点注意的东西)
思路:
这里不好理解的是idx、p的作用。
实际上,idx表示的是当这个前缀不存在时,将它的值赋给给p作为一个序号。
p值相同,代表拥有相同的前缀。
son中存的数代表,我走到这个前缀后,接下来要去哪层找下一个数,如果到这层了发现没有这个数,则先将这个数存在相应的位置上,再给一个idx值,表示找到这个数了,接下来要去哪层找下一个数。son中有值就代表有这个数,并且给出了找到这个数后,接下来一个数要去哪层找。
举个例子,用abc,abd构建前缀数;
- 输入abc:
输入a,son[0][0] = 0,表示没有这个值,idx = 1赋值给p;son[0][0] = 1,表示第0层的a存在了,找到a后,要去第1层找下一个数;
输入b,son[1][1] = 0,表示该位置不存在这个值;idx = 2赋值给p,son[1][1] = 2,表示第1层的b存在了,并且表明,找到ab后,下一个数要去第2层找;
输入c,son[2][2] = 0,表示该位置不存在这个值;idx = 3赋值给p,son[2][2] = 3,表示2层的c存在了,并且表明,找到abc后,下一个数要去第3层找; - 输入abd
输入a,son[0][0] = 1,表示有这个值,找到a了,son[0][0] = 1给出了下一个数要去son中给定的数1中找;
我现在在son[1][]中,输入b,得到son[1][1] = 2,表示该位置存在b,找到了,son[1][1] = 2,给出了下一个数要去son中给定的层2中找;
我现在在son[2][]中,输入d,得到son[2][3] = 0,表明该位置不存在d,idx = 4赋值给p,son[2][3] = 4,表示2层的d存在了,并且表明,找到abd后,下一个数要去第4层找;
idx和p的一种解释
发现了吗?p的值与字符出现的顺序没有关系,不是abc三个字符,a在第0层,b在第1层,c在第2层。p的值是idx赋予的,而idx是在赋值后自增的,因此idx只代表前缀树中现在有多少个不同的前缀,如现在idx最大赋值为4,表示前缀树中有4个不同的前缀
a , ab, abc,abd。 而p只表示具有相同前缀的字符串,即遍历到相同p层的字符串,拥有相同的前缀。
3. 解法
---------------------------------------------------解法---------------------------------------------------
#include<iostream>
using namespace std;
const int N = 1e5+10;
// son存储trie树中的所有儿子节点,cnt表示以当前节点结尾的单词有多少个
// 下标是0的节点,即是根节点又是空节点
int son[N][26],cnt[N],idx;
char str[N];
void insert(char *str){
int p = 0; // 从根节点开始从前往后遍历
// 字符串结尾默认是'\0',因此可以用str[i]判断是否走到结尾
for(int i = 0;str[i];i++){
int u = str[i] - 'a'; // 将a~z转换成0~25
// 如果该位置不存在,则创建这个节点
// 为什么是++idx?用idx表示层数并赋值给p,拥有相同p的,具有相同的前缀
if(!son[p][u]) son[p][u] = ++idx;
// 走到下一个节点
p = son[p][u];
}
cnt[p] ++; // 表示以这个节点(p)为单词的数量多了一个,cet[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 ;
cin >> n;
while(n--){
char op[2];
/*
为什么不用加&?
根据scanf函数的定义,其接收元素必须是类型与控制字符串元素对应的变量的地址。
&是取地址操作符,当接收元素不是地址时要用&获得变量的地址,当接收元素已经是地址时就不用&了,
而数组名就是地址
*/
scanf("%s%s",op,str);
/*
*op是什么意思?
数组名就是首地址,*代表取这个首地址的值
*op 和 op[0] 具有大致相同的效果
*/
if(*op == 'I') insert(str);
else printf("%d\n",query(str));
}
return 0;
}
可能的问题
- 为什么不用加&? 根据scanf函数的定义,其接收元素必须是类型与控制字符串元素对应的变量的地址。&是取地址操作符,当接收元素不是地址时要用&获得变量的地址,当接收元素已经是地址时就不用&了。而数组名就是地址。
- *op是什么意思?
*数组名就是首地址,*代表取这个首地址的值, op 和 op[0] 具有大致相同的效果
4. 可能有帮助的前置习题
5. 所用到的数据结构与算法思想
6. 总结
前缀树用数组实现的模板题,推荐完全背下来。