[AcWing]835. Trie字符串统计(C++实现)前缀树模板题

1. 题目

在这里插入图片描述

2. 读题(需要重点注意的东西)

思路:
这里不好理解的是idx、p的作用。
实际上,idx表示的是当这个前缀不存在时,将它的值赋给给p作为一个序号。
p值相同,代表拥有相同的前缀。
son中存的数代表,我走到这个前缀后,接下来要去哪层找下一个数,如果到这层了发现没有这个数,则先将这个数存在相应的位置上,再给一个idx值,表示找到这个数了,接下来要去哪层找下一个数。son中有值就代表有这个数,并且给出了找到这个数后,接下来一个数要去哪层找。

举个例子,用abc,abd构建前缀数;

  1. 输入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层找;
  2. 输入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;
}

可能的问题

  1. 为什么不用加&? 根据scanf函数的定义,其接收元素必须是类型与控制字符串元素对应的变量的地址。&是取地址操作符,当接收元素不是地址时要用&获得变量的地址,当接收元素已经是地址时就不用&了。而数组名就是地址。
  2. *op是什么意思?
    *数组名就是首地址,*代表取这个首地址的值, op 和 op[0] 具有大致相同的效果

4. 可能有帮助的前置习题

5. 所用到的数据结构与算法思想

6. 总结

前缀树用数组实现的模板题,推荐完全背下来。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cloudeeeee

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值