【C++】字典树

文章目录前言简介前言这是唯一一个我到现在写了的不能做A+B的数据结构,如能,还请神犇指教……想一想,百度的数据总量早已超过1TB=1024GB=1048576MB1TB=1024GB=1048576MB1TB=1024GB=1048576MB,是怎么为你迅速地查出来字典树的?当然是使用字典树。简介这是一棵字典树的图片:在这个字典树中有两种点,红点和白点,后面都会解释。这个字典树中...
摘要由CSDN通过智能技术生成


前言

这是唯一一个我到现在写了的不能做A+B的数据结构,如能,还请神犇指教……
想一想,百度的数据总量早已超过 1 T B = 1024 G B = 1048576 M B 1TB=1024GB=1048576MB 1TB=1024GB=1048576MB,是怎么为你迅速地查出来字典树的?
当然是使用字典树。

简介

这是一棵字典树的图片:

在这个字典树中有两种点,黑点和白点,后面都会解释。
这个字典树中存的就是abc, abcd, abd, b, bcd, efg, hij7个(单词??)字符串。在这棵树里面,如果搜索aab,就可以给出abc, abcd, abd三个字符串的结果。
可以看出,aab是这三个单词的前缀,所以字典树是一种前缀树
可以看看这个基本信息表:

学名 别名 类型
字典树 单词查找树,Trie树 树形数据结构

基本性质

  1. 根节点不包含字符,除根节点外每一个节点都只包含一个字符;2. 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串;
  2. 每个节点的所有子节点包含的字符都不相同。
    可以对照一下上图中的字典树看一看是否符合。

实现

定义

首先,我们要明确字典树是一棵树,树是一种图,所以在字典树的定义上,我们采用类似邻接表的方法。
因为在竞赛中一般字典树记录的都是字母或数字,所以可以用二维数组记录,第一维记录当前节点的编号,最大就是所有字符串的长度和(百度:我有几个TB我也不知道),第二维记录边权,也就是这条边所表示的字符,值记录连接到的另外一个点。
一个最多有 1 0 5 10^5 105个节点的记录小写字母的字典树如下定义:

int trie[100001][26], k;
//理解trie[p][c]:编号为p的点走到编号为trie[p][c]的点需要c的路程

k记录的是当前这棵树有多少个节点。
又因为有黑点和白点之分,就还需要一个数组来记录,即:

bool col[100001];

至于这个区别在哪里,请听下回讲解。

插入

再回顾一下前面那张图:


这个字典树中存的就是abc, abcd, abd, b, bcd, efg, hij7个字符串。

容易发现黑点表示的是一个字符串结束的点。
下面演示在这个字典树中插入字符串bcg的过程(红点表示当前遍历到的点):

首先,找到根节点。
在这里插入图片描述
然后,跟着原先已有的路径向下遍历。
在这里插入图片描述
在这里插入图片描述
走到了点7,已经完成插入的已经有bc这个前缀子串了,但是,cg没有路!
怎么办?再创建一个新点,即点15。
在这里插入图片描述
注意这个时候我们是站在点7的角度上创建点15这个新点,至于怎么创建新点,搞懂了定义就很简单。

再把点15标黑,因为点15是字符串bcg的结束节点。于是最终完成时的图长这样:
在这里插入图片描述
从图片模拟到代码实现:
考虑将当前遍历到的点用一个数字 p p p来表示,那么我们知道了边权,也就是当前点连接到的字符 c c c,我们也就可以得到连接的另一个点,也就是 t r i e [ p ] [ c ] trie[p][c] trie[p][c]

当然,因为有效字符有96个之多,这样很容易爆空间,所以毒瘤出题人一般都会给出字符的范围,假设最小为 x x x,那么另一个点也就是 t r i e [ p ] [ c − x ] trie[p][c-x] trie[p][cx]

因为开始 t r i e trie trie的所有值都为0,又因为没有一个点会连到根,所以如果 t r i e [ p ] [ c ] trie[p][c] trie[p][c]为0,就证明原先没有从 p p p点经过字符 c c c的路径,这个时候就可以建一个新点了。

最后,对插入的字符串的每一个字符进行遍历,依次插入每一个字符,最后记得将字符串的最后一个字符对应的点标黑。

还是那一棵记录小写字母构成的字符串的字典树:(注意insert是关键字)

inline void Insert(char* s) {
   
    int len = strlen(s), p = 0;
    for(int i = 0; i < len; i++) {
   
        char c = s[i] - 'a';
        if(!trie[p][c]) trie[p][c] = ++k;
        p = trie[p][c];
    }
    col[p] = 1;
}
inline void Insert(string s) {
   
    int len = s.length(), p = 0;
    for(int i = 0; i < len; i++) {
   
        char c = s[i] - 'a';
        if(!trie[p][c]) trie[p][c] = ++k;
        p = trie[p][c];
    }
    col[p] = 1;
}

附:这样一个板子几乎可以解决任何的字典树题:

inline Type funtion(char* s) {
   
    int len = s.length(), p = 0;
    for(int i = 0; i < len; i++) {
   
        char c = s[i] - x;//x为最小的可能字符
        //do sth. ...
        p = trie[p][c];
    }
    //do sth. ...
}

查询

一般的查询有3种:查询是否存在这个字符串,查询是否有这个字符串的前驱字符串,查询是否有这个字符串的后缀字符串。
因为都可以用板子,下面一一亮出代码:

inline bool Search1(char* s) {
   //查询是否存在这个字符串
    int p = 0
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值