Trie树入门
Trie树入门
貌似很多人会认为\(Trie\)是字符串类型,但是这是数据结构!!!。
详情见度娘
下面开始进入正题。
PS:本文章所有代码未经编译,有错误还请大家指出。
引入
先来看一个问题
给定一本字典中的\(n\)个单词,还有\(m\)个询问。每次询问询问一个单词是否出现在这\(n\)个单词中。
暴力
最简单的就是暴力做法啦,我们直接枚举去判别对应位置,还可以再加点优化。
即:长度不同,肯定不是同一个单词。
for(int l;m;m--)
{
bool flg=false;
scanf("%s",str);
l=strlen(str);
for(int i=1;i<=n;i++)
{
int len=strlen(s[i])
if(l!=len)continue;
for(int j=1;j<=len;j++)
{
if(s[i][j]!=str[j])
{
flg=true;
break;
}
}
if(flg)
{
for(int j=1;j<=len;j++)
printf("%c",s[i][j]);
}
putchar('\n');
}
}
时间复杂度为\(O(n\times m\times len)\)
加强
很容易发现暴力不是很好写。而且数据范围再大一点的话就根本跑不动,而且存不下。
当数据范围加强到刚刚好直接存储,存储不下的时候。我们就要考虑改进。
那么如何改进呢?
深入
假如给定我们的\(n\)个单词是这样的。
\]
那么我们考虑这样一个问题
Q:空间浪费在了哪里?
A:容易发现的是,在保证单词不乱序的情况下,他们的部分前缀是相同的!那么我们是不是可以从这里入手来减少空间复杂度呢?
当然可以啊!这个时候我们需要联想到树的性质:
- 相同的可以压缩成一个节点(那我们就能将这些前缀压缩来减少空间消耗)
- 树上路径唯一。(那我们就可以保证每一个单词之间的存储互不影响)
由此,我们便引出了\(Trie\)树
浅谈Trie树
简介
Trie是一种多叉树,在对单词处理中是经常利用的一种数据结构,是树结构中一种较为特殊的树结构,它在计算机中对字符采用链表方式存储 。
---百度百科
基本性质
Trie的核心思想是空间换时间,利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。
它有3个基本性质:
根节点不包含字符,除根节点外每一个节点都只包含一个字符。
从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
每个节点的所有子节点包含的字符都不相同。
---百度百科
形态
一般的Trie有两种存储方式:
- 将字符存储在边上
- 将字符存储在点上
这里给出将字符存储在点上的形式

大概就是这个样子啦。
这样从根节点到任何一个子节点就都是一个存储的单词啦。
图中所存储的就是这些单词咯
\]
(由于时间问题就没有作一颗比较大的Trie,见谅。)
构建
知道了\(Trie\)的一些简单东西。
那么我们考虑如何构建一棵\(Tire\)。
显然我们需要将一个单词完整存储就需要遍历整个单词来存储每一位字母。
并且我们需要从浅入深地遍历这棵树。
写法可以为递归版本,也可以为非递归版本。
这里给出非递归版本。
code
void ins(char *s)
{
int rt=0;
for(int i=0,v;s[i];i++)
{
int v=s[i]-'a';
if(!trie[rt][v])
trie[rt][v]=++tot;//给每个节点一个编号,tot为全局变量
rt=trie[rt][v];//向下一个节点,继续存储当前单词
}
}
由我们构建函数可知,Trie的空间复杂度为(单词长度 \(\times\) 字符种类 )。
且这个构建过程的时间复杂度为\(O(n^2)\)
时间复杂度证明,不会 emmm。
查询操作
Trie树一般支持两种查询操作:
- 查询单词是否出现过。
- 查询单词的出现次数。
有些时候,根据写法的不同,我们还可以判断一个单词是不是某个单词的前缀。
当然如果你构建后缀的Trie的话,还可以判断这个单词是不是某个单词的后缀。而这个树又名为后缀树。
对于后缀树这里不进行研究。(等我有时间会写的。)
不论是哪种查询操作,我们都需要遍历整棵树来判断是否存在这些节点。
依旧有两种写法,这里给出非递归版。
code
bool find(char *s)
{
int rt=0;
for(int i=0,v;s[i];i++)
{
v=s[i]-'a';
if(!trie[rt][v])retturn false;
rt=trie[rt][v];
}
if(ok[rt])return true;
//加上这一句可以判断当前单词是否出现过。
//而如果不加,就可以判断当前单词是不是某个单词的前缀
//如果加上这一句的话就在构建的时候对应的在词尾标记即可。
return false;
}
根据\(m\)个询问,再进行遍历,显然这样的时间复杂度为\(O(n^2)\)。
而Trie的应用还有很多。这里不一一介绍了,毕竟我是个退役选手啊。
具体的完整代码详见例题部分。
后面继续深入学习的话还有01Trie,可持久化Trie.
下面讲解一下01Trie
01Trie
有一类问题叫做01字典树问题,它是用来解决xor的有力武器,通常是给你一个数组,问你一段连续的异或和最大是多少,正常思路贪心dp啥的都会一头雾水,但是用01字典树就能很快的解决,实现起来也十分方便(其实和trie树差不多。
实现
01字典树的实现可以看成是把一个数的二进制字符化后插入到一棵一般的字典树中。
那你们会不会有疑问就是说 当某一个数的二进制位是另一个数的二进制位的某一前缀的时候 是不是查不到这个数
\]
然后这个时候 我们可以做一个处理 即 从一个特别特别长的位置开始插入(一般会选择 1<<30位开始处理 ,对应:01字典树种插入3时 相当于在字典树中插入00 …..00011)
但还是要见题而异
操作
当我们查询最大异或值的时候,我们从最高位向下贪心查找。
贪心策略:
当前查找第k位,二进制数位x,如果存在x^1的节点,我们就进入这个节点,否则进入x节点。
证明:
这个不太会用文字来叙述,直接看一下比较。
\]
很明显,当前对应的最高位上是\(1\),比它后面位上全是\(1\)要大。
因此我们的策略是正确的。
性质
对应01Trie的性质与Trie的相似,这里不再赘述。
而我们引入一个重要的性质:
如果要求[l,r]的异或和:
由\(X \ xor \ X = 0;0 \ xor\ Y = Y;\)
推知:所有\([l,r] = [1,r] XOR[1,l - 1]\)
构建
同样会有两种写法,这里给出非递归版本的代码
code
void ins(int x)
{
int rt=0;
for(int i=1<<30;i;i>>=1)
{
bool c=x&i;
//这里推荐写bool类型,不推荐写int类型,因为很容易忘记是对应位为1
if(!trie[rt][c])
trie[rt][c]=++tot;//记录节点编号
rt=trie[rt][c];
}
}
应该不是很难理解,不作详细解释。
查询最大异或和
这里是查询一个数的最大异或和。
同样给出非递归版本的代码。
code
int query(int x)
{
int ans=0,rt=0;
for(int i=1<<30;i;i>>=1)
{
bool c=x&i;
if(trie[rt][c^1])
ans+=i,rt=trie[rt][c^1];
//如果有我们就选择这个较高位来实现我们的贪心策略.记得累计答案
else rt=trie[rt][c];
}
return ans;
}
小结
总的来说,在这有限的几个小时我也就只能写这么多吧。
再深入一些还是需要大家后期的学习。如果能帮助到大家很高兴。有不足之处还请大家多多指出。
例题
这些例题就不做深入剖析,有些我是写过题解的,都会给出链接。
如果觉得很麻烦就来我博客园链接好了.
Trie树
题目:TJOI2010 阅读理解 题解:这
题目:UVA11362 Phone List 题解:这
01Trie
这些题目包括可持久化01Trie还有普通01Trie
题目:p4551 最长异或路径 题解:这
下面两个是可持久化01Trie
题目:TJOI2018 异或 题解:这
题目:p4735 最大异或和 题解:这
最后的话
感谢洛谷提供的题目。为洛谷打call!!
感谢百度百科提供的一些介绍。
原生态顾z创作。
Trie树入门的更多相关文章
- Trie树入门及训练
什么叫Trie树? Trie树即字典树. 又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种.典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本 ...
- HDOJ1251-统计难题(trie树入门)
统计难题 Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 131070/65535 K (Java/Others) Total Subm ...
- hdu 1251 统计难题(trie树入门)
统计难题 Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 131070/65535 K (Java/Others)Total Submi ...
- hdu1251(Trie树)
传送门:统计难题 分析:Trie树入门题,随便写写练下手感,统计每个节点被多少单词经过就可以了. #include <iostream> #include <cstdio> # ...
- HDU1251 统计难题 【trie树】
统计难题 Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 131070/65535 K (Java/Others) Total Subm ...
- 字典(trie)树--从入门到入土
今天再来认识一个强大的数据结构. 字典树又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种.典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词 ...
- trie树--详解
文章作者:yx_th000 文章来源:Cherish_yimi (http://www.cnblogs.com/cherish_yimi/) 转载请注明,谢谢合作.关键词:trie trie树 数据结 ...
- 转:trie树--详解
前几天学习了并查集和trie树,这里总结一下trie. 本文讨论一棵最简单的trie树,基于英文26个字母组成的字符串,讨论插入字符串.判断前缀是否存在.查找字符串等基本操作:至于trie树的删除单个 ...
- 数据结构~trie树(字典树)
1.概述 Trie树,又称字典树,单词查找树或者前缀树,是一种用于快速检索的多叉树结构,如英文字母的字典树是一个26叉树,数字的字典树是一个10叉树. 我理解字典树是看了这位大佬博客.还不了解字典树的 ...
随机推荐
- Global Vectors forWord Representation
参考论文: GloVe: Global Vectors forWord Representation 参考博客:https://blog.csdn.net/coderTC/article/detail ...
- Linux下如何卸载软件(Debian系)
说明:此方法适用于Debian.Ubuntu等带apt工具的操作系统. 步骤: 1.首先我们需要知道将要卸载的软件名称,比如我现在打算卸载tightvncserver,但是如果你不确定名称,没关系,可 ...
- 2017 国庆湖南Day2
期望得分:100+30+100=230 实际得分:100+30+70=200 T3 数组开小了 ..... 记录 1的前缀和,0的后缀和 枚举第一个1的出现位置 #include<cstdio& ...
- 重构改善既有代码设计--重构手法16:Introduce Foreign Method (引入外加函数)&& 重构手法17:Introduce Local Extension (引入本地扩展)
重构手法16:Introduce Foreign Method (引入外加函数)你需要为提供服务的类增加一个函数,但你无法修改这个类.在客户类中建立一个函数,并以第一参数形式传入一个服务类实例. 动机 ...
- ① 设计模式的艺术-01.单例(Singleton)模式
单例模式为何要出现 在工作过程中,发现所有可以使用单例模式的类都有一个共性,那就是这个类没有自己的状态,换句话说,这些类无论你实例化多少个,其实都是一样的. 如果我们不将这个类控制成单例的结构,应用中 ...
- 二. Jmeter--关联
1. 首先建立一个线程组(Thread Group),为什么所有的请求都要加入线程组这个组件呢?不加不行吗?答案当然是不行的.因为jmeter的所有任务都必须由线程处理,所有任务都必须在线程组下面创建 ...
- Mac Sublime Vim模式 方向键无法长按
终端输入 sublime2: defaults write com.sublimetext.2 ApplePressAndHoldEnabled -bool false sublime3: defau ...
- $fhqTreap$
- $fhqTreap$与$Treap$的差异 $fhqTreap$是$Treap$的非旋版本,可以实现一切$Treap$操作,及区间操作和可持久化 $fhqTreap$非旋关键在于分裂与合并$(Sp ...
- UVA 1103 How Many O's?
题目链接:UVA-11038 题意为给定n和m,求n和m之间(包含)的所有数包含的0的个数. 思路是,用cal(x)表示小于等于x的数包含的0的个数.则答案为cal(n)-cal(m-1). 再把求c ...
- angular中使用AMEXIO
1.用NPM添加依赖到项目中,amexio需要先添加以下四个依赖到项目 npm install jquery@3.2.1 --save npm install bootstrap@4.0.0-alp ...