Trie树又称为字典树,这个数据结构的意义是高效地存储和查询字符串。我们先来看一下它的思想。

关于树的定义

为了方便我们后面的理解,我先把后面会用到的一些名词介绍一下。
(有时输入法会把结点和节点打错,故以下的“节点”如无特殊说明,都可看作是“结点”)
孩子结点:一个结点的子树的根结点就是该结点的孩子结点。
父亲结点:一个结点如果有孩子结点,那么它就是它的孩子结点的父亲结点。
兄弟结点:与该结点同父亲的结点称为该结点的兄弟结点。
堂兄弟结点:同一层次非同父亲的结点
祖先节点和子孙结点:如果存在一条从结点X到结点Y的从上至下的路径,那么称结点X是结点Y的祖先结点,结点Y是结点X的子孙结点。注意:自己是自己的祖先结点,也是自己的子孙结点。

Trie树的思想

假设给定一个字符串,我们要存储这个字符串。Trie树的算法是,从根节点开始,向它的孩子结点不停查找,如果第i层(设根节点为第0层,从第1层开始存储)有字符串的第i个字母结点,且这个结点是前一个字母的孩子结点,那么我们直接选择这个结点继续向下查找;如果第i层没有字符串的第i个字母结点,那么我们就在这一层创建一个结点来存储这个字母。以此类推,直到遍历完整个字符串,这时我们在遍历停止的这个结点做一个标记,统计值+1,代表这个字符串已经存储进Trie树里了,如果之前树里已经存储过该字符串,那么该字符串的统计值就会大于1。

如果我们要查找一个字符串,和存储字符串的操作基本相同。同样是从根节点开始向下一层一层地查找,如果第i层没有字符串的第i个字母结点,直接返回查找失败;如果有的话就继续向该结点的孩子结点查找,直到遍历完字符串,这时我们返回该终止结点的统计值,也就是该字符串在给定集合中出现的次数。
画个图来演示一下:
假如说有如下字符串:
abcde
acd
我们先存abcde:

第一步,我们先查找根节点的孩子结点,看是否有‘a’,结果没有,所以我们在根节点的下一层创建一个结点,存储‘a’。
第二步到第五步同理。一直到存储‘e’结点时,我们发现我们已经遍历完了整个字符串abcde,于是,我们在e这个结点上记录它的统计值,表示这个字符串在该Trie树里记录了几次。由于e这个结点之前从未被记录过,所以它目前的统计值是0+1=1,代表abcde这个字符串在集合里一共只出现了一次。

然后我们存储acd:

第一步,我们从根节点向它的孩子结点查找,发现‘a’结点已经存在了,那我们直接选择这个a结点,继续向下;
第二步,我们在a结点的下一层发现,只有b,没有c,所以我们要创建一个c结点,并顺着c结点继续向下。
第三步,我们发现c结点的孩子结点里什么都没有,所以我们创建一个d结点。这时,acd已遍历完毕,于是我们把d结点的统计值+1。

至此,关于abcde acd集合的Trie树就建立完成了。
值得注意的是,有两个结点存储了特殊的信息,那就是给定字符串在该集合中出现的次数,我在下图中用红笔标记出来。

这就是Trie树的全部思想了。可以看出来,它有两个主要操作:插入和查找,对应的代码如下:

const int N=100010;
int idx,son[N][26],cnt[N];
char str[N],c;
void myInsert(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];
}

其中,cnt数组就是我们刚才提到的,存储“统计值”的数组,idx起到一个指引的作用,表示我们当前用到的结点位置;son[p][u]中的p代表当前结点的父亲结点位置,u代表当前正在查询的字符串中的字符,son[p][u]的值代表该结点的位置,如果该结点不存在,则值为0。

下面我们来看一道题巩固一下:

Trie字符串统计
维护一个字符串集合,支持两种操作:

“I x”向集合中插入一个字符串x;
“Q x”询问一个字符串在集合中出现了多少次。
共有N个操作,输入的字符串总长度不超过 105105,字符串仅包含小写英文字母。

输入格式

第一行包含整数N,表示操作数。

接下来N行,每行包含一个操作指令,指令为”I x”或”Q x”中的一种。

输出格式

对于每个询问指令”Q x”,都要输出一个整数作为结果,表示x在集合中出现的次数。

每个结果占一行。

数据范围

1≤N≤2∗1041≤N≤2∗104

输入样例:

5
I abc
Q abc
Q ab
I ab
Q ab
输出样例:

1
0
1

AC代码如下:

#include <bits/stdc++.h>
using namespace std;
const int N=100010;
int idx,son[N][26],cnt[N];
char str[N],c;
void myInsert(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()
{ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int n;cin>>n;while(n--){cin>>c>>str;if(c=='I'){myInsert(str);}if(c=='Q'){cout<<query(str)<<'\n';}}return 0;
}

ACWING143 最大异或对
在给定的 N 个整数 A1,A2……AN 中选出两个进行 xor(异或)运算,得到的结果最大是多少?

输入格式
第一行输入一个整数 N。

第二行输入 N 个整数 A1~AN。

输出格式
输出一个整数表示答案。

数据范围
1≤N≤105,
0≤Ai<231
输入样例:
3
1 2 3
输出样例:
3

思想
这道题的目的是为了说明一件事情:Trie树不仅可以处理字符串,也可以处理二进制的数。而计算机中的数据类型都可以转化为二进制数,因此,Trie树理论上可以处理所有数据类型。

我们先来看看输入样例:
1:二进制是01
2:二进制是10
3:二进制是11
从直观上来看,三者异或,值最大的结果一定是11。
那么如何用Trie树来处理这个问题呢?
存储数据的方法和经典Trie树一样,这里不再赘述。值得一提的是,因为是二进制数,所以son数组的第二维空间只要有2就够了(0和1);并且存储要从最高位开始存储,原因我们后面再说。

我们重点说一下查找函数。

在这道题中,我们的目的是找出最大异或对,这就要求1所在的位数要尽可能高,在此基础上1的个数要尽可能多(100也比011要好,因为前者虽然1的个数小于后者)

这就要求我们从最高位开始查找(如果最高位是1,肯定优先采纳),所以我们的存储和查找都要从最高位开始。

查找核心思想:
我们从根结点开始查找,如果发现有和该结点数值相反(0相反是1,1相反是0),那就代表二者最终异或结果一定是1;如果没有的话,那么该位运算的最终结果就是该结点的数值本身。

这样我们就清楚了基本的思想,具体代码如下:

#include <bits/stdc++.h>
using namespace std;
const int N = 100010, M = N * 31;
int son[M][2], idx;
void myInsert(int x)
{int p = 0, u;for(int i = 30; i >= 0; i--){u = x >> i & 1;if(!son[p][u])son[p][u] = ++idx;p = son[p][u];}
}
int query(int x)
{int p = 0, res = 0, u;for(int i = 30; i >= 0; i--){u = x >> i & 1;if(son[p][!u]){p = son[p][!u];res = res * 2 + 1;}else{p = son[p][u];res = res * 2;}}return res;
}
int main()
{ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int n, res = 0, num;cin >> n;while(n--){cin >> num;myInsert(num);res = max(res, query(num));}cout << res << '\n';return 0;
}

Trie树讲解(例题:ACWING 835,ACWING 143)相关推荐

  1. 【线段树】【模板】讲解 + 例题1 HDU - 1754 I Hate It (点修改分数)+ 例题二 POJ - 3468 A Simple Problem with Integers(区间加值)

    [线段树][模板]讲解 + 例题1 HDU - 1754 I Hate It (点修改分数)+ 例题二 POJ - 3468 A Simple Problem with Integers(区间加值) ...

  2. SZUACM集训字符串基础总结: 字符串最小表示 ,KMP, EXKMP, Manracher, Trie树,字符串的hash; 附带一写常见的运用技巧,邝斌大佬的板子和例题[持续更新]

    第一部分 字符串的匹配<-------->KMP 模式匹配:子串的定位运算称为串的模式匹配或串匹配. 假设有两个串S,T,设S为主串,也称正文串,T为子串,也称为模式,在主串S中查找与模式 ...

  3. 0x16.基本数据结构 — Trie树(字典树)+ A C 自 动 机

    目录 用TrieTrieTrie树来处理整数异或问题是真的舒服! 一.TrieTrieTrie树 TrieTrieTrie的基本操作 0.初始化 1.插入 2.检索 二.TrieTrieTrie树例题 ...

  4. 【图解算法】Trie树

    欢迎来到我的算法专栏,今天我们来讲一种常见的数据结构:Trie树. 我们会对Trie树的性质,相关操作,应用进行讲解,并模拟实现一个简版的Trie树. 目录 1. Trie树 简介 2. Trie树的 ...

  5. BZOJ3261 最大异或和 解题报告(可持久化Trie树)

    本题链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3261 题目描述 给定一个非负整数序列{a},初始长度为N. 有M个操作,有以下两种操作类 ...

  6. trie树java_【数据结构】Trie树的应用:查询IP地址的ISP(Java实现)

    查询IP地址的ISP 给定一个IP地址,如何查询其所属的ISP,如:中国移动(ChinaMobile),中国电信(ChinaTelecom),中国铁通(ChinaTietong)? 现在网上有ISP的 ...

  7. trie树 mysql_Trie树详解(转)

    特别声明 本文只是一篇笔记类的文章,所以不存在什么抄袭之类的. 以下为我研究时参考过的链接(有很多,这里我只列出我记得的): 1.字典树的概念 字典树,因为它的搜索快捷的特性被单词搜索系统使用,故又称 ...

  8. IP trie树接口

    自己实现了一个IP trie树接口. 在这里保存一下,方便备份以后使用,同时欢迎纠错和交流,希望有大神能指教更高效的算法. 1.头文件如下(iptrie.h) 1 #ifndef _IP_TRIE_H ...

  9. 数据结构与算法之美笔记——基础篇(下):图、字符串匹配算法(BF 算法和 RK 算法、BM 算法和 KMP 算法 、Trie 树和 AC 自动机)

    图 如何存储微博.微信等社交网络中的好友关系?图.实际上,涉及图的算法有很多,也非常复杂,比如图的搜索.最短路径.最小生成树.二分图等等.我们今天聚焦在图存储这一方面,后面会分好几节来依次讲解图相关的 ...

  10. 算法学习之Trie树

    算法学习之Trie树 by MPS [定义]   trie树又名字母树,是针对字符串的匹配,查找的一种高效手段,是哈希表的一种变种,但青出于蓝胜于蓝.我这个蒟蒻也是表示啃了两天才啃出来这些皮毛,当做学 ...

最新文章

  1. 基于linux的netfilter处理数据包的过程分析,基于Linux的Netfilter处理数据包的过程分析...
  2. kmeans python interation flag_Python / Scipy Integration数组
  3. python opencv 保存摄像头视频,以及fourc编码的介绍
  4. centos 6.5 配置LDAP服务器+客户端!
  5. oracle删除表中数据_工作表中提供的特定数据在数据库中批量删除的方法
  6. 图片任意放大缩小 插件
  7. php输出1到10的和,php通过排列组合实现1到9数字相加都等于20的方法
  8. 华为鸿蒙系统学习笔记5-华为方舟编译器正式开源及相关源码下载
  9. mano安全_安全接入——后台
  10. 图片 滚动切换效果(五) 高级篇
  11. 数据交换-电路/报文/分组交换
  12. vue生成txt文件下载
  13. 计算机控制实验 实验十,自控计控原理实验箱
  14. 将[c语言中文网](http://c.biancheng.net/view/1380.html)的“”23 种设计模式“的网页转成pdf
  15. Kettle计算器常用函数
  16. 批量安装系统工具cobbler配置
  17. JavaScript 数组塌陷
  18. 【老生谈算法】matlab实现Kmeans聚类算法源码——Kmeans聚类算法
  19. 联发科MT6795和MT6797处理器哪个好?性能上有什么区别?
  20. 微信公众号,统计公众平台的打赏人数和金额脚本

热门文章

  1. 100%会问的float?
  2. Java并发编程之原子类
  3. git rebase之前需要 commit 才行
  4. mysql主主互备模式配置
  5. 《R in Action》读书笔记(2)
  6. Windows Server 2003 R2中的DFS复制与管理
  7. ADSL桥接模式和路由模式的区别(转)
  8. spring @POSTConstruction注解的作用
  9. [转]虚函数实现原理
  10. volatile分析