暑期算法集训之Trie(字典树)篇:Acwing学习笔记->数据结构——字典树

目录

引言

1、基础概念

2、字典树的存储

 3、字典树的查找

 例题:字符串统计——Acwing


引言

在计算机科学中,Trie(字典树)是一种高效的数据结构,通常用于高效的存储和检索字符串集合。它通过利用字符串之间的公共前缀,提供了快速的查找和插入操作。本文将介绍 Trie 的基本概念、结构和应用场景,并通过示例代码来解释它的使用方法。

1、基础概念

我们先来看一个完整的树大概是什么样子的:

我们可以看到,树的结构就是从最上面的节点一直产生分支,不断产生新的节点,此时,我们需要知道,在数树结构中,存在这样两个名词:根节点(root),子节点(child node)

提取出最上面三个节点,我们可以在树形结构中如下图所示:

其实对于最上面的A节点来说,下面的所有节点(B、C、D、E、F、G、H、I、J、K、L、M、N、O)都是A的子节点

为什么这么说?我们不妨想象一下,如果在某一情况下,当B作为根节点呢?很显然,B以下的部分(D、E、H、I)就都是B的子节点

因此,关于根节点和子节点的位置,是通过某一状态来判断的,切不可单纯认为最上面的就是根节点,下面都是子节点,永远不会变了。 

2、字典树的存储

比如我们要在Trie中存储这样的字符串集合,用字典的方式

abcdef

abdef

aced

bcdf

bcff

cdaa

bcdc

当我们需要将这些字符存入到一颗树中,我们可以从第一个字符串开始,从第一个根节点开始存储。

首先在一开始,我们的树是空的,只存在一个root根节点(可以视为一直存在),然后当我们从第一个字符串的第一个字母开始存储时,我们会率先查找树的两个子节点是否已经存储过这个字符,如果没有,我们就创建一个子节点来存储这一个字符,如果有,我们就转移到树的存储该字符的那一个节点上,以它为新的根节点,然后开始存储第一个字符串的第二个字符......

我们画图演示一下:(在存储时,默认先左节点再右节点

存储abcdef:                                                        存储abdef:

存储aced时:                                                        存储bcdf时:

 存储bcff时:                                                        存储cdaa时:

 存储bcdc时:

我们将完整的树画下来:

至此,我们的字符串集合已经全部存入到这一颗树当中去了,并且我们可以发现,字符串集合中的每一个字符串,都可以在树中通过对应的路径得到。

下面,我们需要对最后一层节点做一个标记:

有什么作用呢?他表示的是在字符串集合中,每一个字符串结尾的位置

我们回到刚刚每一条字符串,然后依次在树中进行查找,可以发现每一个字符串的结尾,在这一棵树中都是最后一层的位置,也就是说,我们可以通过这些标记过的位置往回走,一样可以得到原来的字符串。

 也许还有人不理解,我们假设我们现在有一个abc的字符串,那我们就可以按照这个路径找到:

那么此时,我们就要对字符串末尾的位置打上标记:

 

 也就是说,只要字符串末尾的字符的标记存在,那么这条字符串的路径必然存在

 3、字典树的查找

当我们构建出树结构后,我们可以通过树的路径来查找某一个字符串是否存在,或者说这个字符串出现过了几次。

假设我们要在上面的那棵树中寻找aced

很明显我们可以通过这样的路径找到,并且最后一个节点是有标记的

但是若是我们要寻找abcf,那很明显,根据同样的道理,我们是找不到的,因为当我们在路径的

a->b->c时,c此时作为根节点,无法找到子节点 f 的存在。

另外还有一种情况,可以详细的描述标记的作用:查找abcd是否存在字符串集合中?

在我们的树中,abcd虽然是可以被找到的,如图:

 

但是!!! 我们发现在d的位置,我们并没有标记过,也就是说在原来的字符串集合中,并没有abcd这样的字符串存在,所以无法查找到该字符串

 例题:字符串统计——Acwing

 代码:

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

const int N=1e5+10;
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];
}

int main(){
    int n;
    cin>>n;
    while(n--){
        char op[2];
        cin>>op>>str;
        if(op[0]=='I') insert(str);
        else cout<<query(str);
    }
    return 0;
}

对于上面的代码,下面给到一个Acwing学员的精彩评论!~~~

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

LifeGPT

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

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

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

打赏作者

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

抵扣说明:

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

余额充值