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\)个单词是这样的。

\[abcaa\\aabca\\abdac\\aabcabd\\bacabd\\ \dots
\]

那么我们考虑这样一个问题

Q:空间浪费在了哪里?

A:容易发现的是,在保证单词不乱序的情况下,他们的部分前缀是相同的!那么我们是不是可以从这里入手来减少空间复杂度呢?

当然可以啊!这个时候我们需要联想到树的性质:

  1. 相同的可以压缩成一个节点(那我们就能将这些前缀压缩来减少空间消耗)
  2. 树上路径唯一。(那我们就可以保证每一个单词之间的存储互不影响)

由此,我们便引出了\(Trie\)树

浅谈Trie树

简介

​ Trie是一种多叉树,在对单词处理中是经常利用的一种数据结构,是树结构中一种较为特殊的树结构,它在计算机中对字符采用链表方式存储 。

​ ---百度百科

基本性质

Trie的核心思想是空间换时间,利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。

它有3个基本性质:

  1. 根节点不包含字符,除根节点外每一个节点都只包含一个字符。

  2. 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。

  3. 每个节点的所有子节点包含的字符都不相同。

    ​ ---百度百科

形态

一般的Trie有两种存储方式:

  1. 将字符存储在边上
  2. 将字符存储在点上

这里给出将字符存储在点上的形式

大概就是这个样子啦。

这样从根节点到任何一个子节点就都是一个存储的单词啦。

图中所存储的就是这些单词咯

\[aba\\abbc\\aab
\]

(由于时间问题就没有作一颗比较大的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树一般支持两种查询操作:

  1. 查询单词是否出现过。
  2. 查询单词的出现次数。

有些时候,根据写法的不同,我们还可以判断一个单词是不是某个单词的前缀。

当然如果你构建后缀的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字典树的实现可以看成是把一个数的二进制字符化后插入到一棵一般的字典树中。

那你们会不会有疑问就是说 当某一个数的二进制位是另一个数的二进制位的某一前缀的时候 是不是查不到这个数

\[7 =111_{(2)} \\3=11_{(2)}
\]

然后这个时候 我们可以做一个处理 即 从一个特别特别长的位置开始插入(一般会选择 1<<30位开始处理 ,对应:01字典树种插入3时 相当于在字典树中插入00 …..00011)

但还是要见题而异

操作

当我们查询最大异或值的时候,我们从最高位向下贪心查找。

贪心策略:

当前查找第k位,二进制数位x,如果存在x^1的节点,我们就进入这个节点,否则进入x节点。

证明:

这个不太会用文字来叙述,直接看一下比较。

\[8=1000_{(2)}\\7=0111_{(2)}
\]

很明显,当前对应的最高位上是\(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 阅读理解 题解:这

题目:p2264 情书 题解:这

题目:UVA11362 Phone List 题解:这

01Trie

这些题目包括可持久化01Trie还有普通01Trie

题目:p4551 最长异或路径 题解:这

下面两个是可持久化01Trie

题目:TJOI2018 异或 题解:这

题目:p4735 最大异或和 题解:这

最后的话

感谢洛谷提供的题目。为洛谷打call!!

感谢百度百科提供的一些介绍。

原生态顾z创作。

Trie树入门的更多相关文章

  1. Trie树入门及训练

    什么叫Trie树? Trie树即字典树. 又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种.典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本 ...

  2. HDOJ1251-统计难题(trie树入门)

    统计难题 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131070/65535 K (Java/Others) Total Subm ...

  3. hdu 1251 统计难题(trie树入门)

    统计难题 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131070/65535 K (Java/Others)Total Submi ...

  4. hdu1251(Trie树)

    传送门:统计难题 分析:Trie树入门题,随便写写练下手感,统计每个节点被多少单词经过就可以了. #include <iostream> #include <cstdio> # ...

  5. HDU1251 统计难题 【trie树】

    统计难题 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131070/65535 K (Java/Others) Total Subm ...

  6. 字典(trie)树--从入门到入土

    今天再来认识一个强大的数据结构. 字典树又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种.典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词 ...

  7. trie树--详解

    文章作者:yx_th000 文章来源:Cherish_yimi (http://www.cnblogs.com/cherish_yimi/) 转载请注明,谢谢合作.关键词:trie trie树 数据结 ...

  8. 转:trie树--详解

    前几天学习了并查集和trie树,这里总结一下trie. 本文讨论一棵最简单的trie树,基于英文26个字母组成的字符串,讨论插入字符串.判断前缀是否存在.查找字符串等基本操作:至于trie树的删除单个 ...

  9. 数据结构~trie树(字典树)

    1.概述 Trie树,又称字典树,单词查找树或者前缀树,是一种用于快速检索的多叉树结构,如英文字母的字典树是一个26叉树,数字的字典树是一个10叉树. 我理解字典树是看了这位大佬博客.还不了解字典树的 ...

随机推荐

  1. CronTrigger 表达式 (调度配置)

    CronTrigger 的用途更广,相比基于特定时间间隔进行调度安排的 SimpleTrigger,CronTrigger 主要适用于基于日历的调度安排.例如:每星期二的 16:38:10 执行,每月 ...

  2. bluebird -1 New Promise方法

    new Promise new Promise(function(function resolve, function reject) resolver) -> Promise 创建一个Prom ...

  3. java学习笔记记录

    Java内存模型: Java虚拟机规范中将Java运行时数据分为六种. 1.程序计数器:是一个数据结构,用于保存当前正常执行的程序的内存地址.Java虚拟机的多线程就是通过线程轮流切换并分配处理器时间 ...

  4. 2017ACM暑期多校联合训练 - Team 1 1006 HDU 6038 Function (排列组合)

    题目链接 Problem Description You are given a permutation a from 0 to n−1 and a permutation b from 0 to m ...

  5. Oracle笔记之序列(Sequence)

    Oracle中序列是一种数据对象,可以视为一个等差数列,我们自增就是一个遍历这个数列的过程,可以取当前值,也可以将当前值自加n后返回,Sequence与表没有太大的关系,有的时候如果表的主键是数值类型 ...

  6. 蓝色的PC端后台管理界面设计模板——后台

    链接:http://pan.baidu.com/s/1o82hXX4 密码:x6le

  7. 无key值的json数组解析

    [    [        {            "cartId": 9223,            "factoryId": 143,          ...

  8. Vue-Module

    由于使用单一状态树,应用的所有状态会集中到一个比较大的对象.当应用变得非常复杂时,store 对象就有可能变得相当臃肿. 为了解决以上问题,Vuex 允许我们将 store 分割成模块(module) ...

  9. 一款线程安全、基本功能齐全的STL

    MiniSTL 目前正在完成一个STL,主要想通过该项目锻炼C++编程.模板编程.熟悉STL.锻炼数据结构和算法能力. 项目的目标是实现STL的几大构件+线程安全.项目过程中主要参考SGI STL源码 ...

  10. Linux进程的创建函数fork()及其fork内核实现解析【转】

    转自:http://www.cnblogs.com/zengyiwen/p/5755193.html 进程的创建之fork() Linux系统下,进程可以调用fork函数来创建新的进程.调用进程为父进 ...