一.Trie的概念

Trie又称字典树,前缀树(事实上前缀树这个名字就很好的解释了Trie的储存方式)

来一张图理解一下Trie的储存方式:(图片来自百度百科)

由这张图我们也可以知道Trie的特点:

  • Trie的根节点是空的
  • 除根节点外,每个节点储存一个单词/字母
  • 也就是说,从根节点到每个单词节点的路径上的所有字母连接而成的字符串就是该节点对应的字符串
  • 每个非叶子结点一般都会被多次使用,以节省遍历时的时间效率
  • 另外,每个节点下面的数字是他们的编号,这个具体在下面再展开

所以读者们不难看出,

Trie是典型的用空间来换时间的做法

二.Trie的操作

Trie的常用操作:插入,查询,删除

(插入,查询为常用操作,删除操作实在少见就不展开讲了)

在这之前我们需要填一下上面的坑:

那些非根节点下面的数字是什么意思?

事实上,在代码中,我们一般把根节点编号为0,然后把其余节点从1开始编号,然后存在一个数组中(数组的用处是储存每个节点的所有子节点,以便于直接使用下标来存取)

再具体一点,在我的代码中,用ch[i][j]来保存i的那个编号为j的子节点,当然,如果ch[i][j]==0,也就代表这个节点不存在,即i没有这个编号为j的子节点

好了,坑填完了

我们来明确一下另外一个数组的概念:val数组

对于需要用到Trie的题目,一般是不会只让你单纯构建一棵Trie的,一般都会有各种奇奇怪怪的要求,就像我们的例题(例题在下面),怎么处理这一些要求?我们可以把他们看作是一些“附加信息”,然后储存起来,val数组就是用来储存附加信息的

那么现在我们就来讲一下插入操作(具体解释都在代码中):

(我的代码一般是没有指针这种不是很友好的东西的,不过这里因为是定义在结构体里面的,比较麻烦,所以干脆写个指针,意思很好懂的,代码中有注解)

#define ll int
#define N 500010
struct Trie{
ll ch[N][],sz,val[N];
//val为附加信息
//这里的ch数组,第二维的大小为26是因为字符串只由小写字母构成,第二维的大小一般是由字符串的组成决定
//sz即为节点编号
Trie(){
sz=;//一开始的时候只有根节点这一个节点
memset(ch[],,sizeof(ch[]));
memset(val,,sizeof(val));
}//这里是初始化
ll idx(char c){return c-'a';}//返回字符c的编号
void insert(char *s,ll v){
//插入操作 ,这里是整份代码中唯一用到指针的地方,因为函数是放在结构体里面 ,所以最好用个指针,其实等价于char s[]
//s代表要插入的字符串,v为附加信息
ll u=,len=strlen(s+);
for(ll i=;i<=len;i++){
ll c=idx(s[i]);
if(!ch[u][c]){//如果节点不存在就插入,不然就继续往下遍历
memset(ch[sz],,sizeof(ch[sz]));
val[sz]=;//中间的节点是没有附加信息的
ch[u][c]=sz++;//新建节点
}
u=ch[u][c];//往下遍历
}
val[u]=v;//插入附加信息,注意,我们一般只在叶子节点插入附加信息,中间的节点一般是没有附加信息的,因为一个非叶子结点,在Trie中一般都会被不同的单词使用到(定义)
}
}tree;

接下来是查找操作,事实上查找操作基本上是和插入操作是差不多的,如果你真的理解了上面的insert操作,可以尝试着在不看我下面代码的情况下去自己写一下,同样,对于这个操作的解释放在代码中

    ll search(char *s){//这里的指针意义同上
//对于这一个查找操作,我们就以最简单的操作来展开:
//查找这个字符串是否在Trie中出现过,如果出现过,返回它的附加信息
ll u=,len=strlen(s+);
for(ll i=;i<=len;i++){
ll c=idx(s[i]);
if(!ch[u][c])return -;
//没有这个节点,返回-1,即代表这个字符串没有在Trie中出现过
//这个返回值得看题目所需要的附加信息来决定,因为这里是不可以与附加信息冲突的,在这里我们假定附加信息为0或正数
u=ch[u][c];//继续向下遍历
}
return val[u];//这个节点存在,返回它的附加信息
}

三.Trie的题目

1.模板题:luogu P2580 于是他错误的点名开始了

题面篇幅有点长,就不贴了,大概讲一下题意:

给你n个字符串以及m个询问,每次询问需要回答:如果该询问的字符串没有在n个字符串里面出现过,输出“WRONG”,如果该询问的字符串出现过而且是第一次询问,输出“OK”,如果该询问的字符串出现过但不是第一次询问,输出“REPEAT”

仔细看一下题目,事实上就是字典树的模板题,对于insert操作和search操作进行一下微调就可以了

主要的调整是,附加信息不在insert操作里面插入,而是在search操作中插入

(在本题中,附加信息为该字符串是不是第一次被询问)

剩下的跟上面的代码内容都差不多了,接下来上代码

#include <cstdio>
#include <cstring>
#define ll int
#define inf 1<<30
#define mt(x,y) memset(x,y,sizeof(x))
#define il inline
il ll max(ll x,ll y){return x>y?x:y;}
il ll min(ll x,ll y){return x<y?x:y;}
il ll abs(ll x){return x>?x:-x;}
il void swap(ll &x,ll &y){ll t=x;x=y;y=t;}
il void read(ll &x){//快读
x=;ll f=;char c=getchar();
while(c<''||c>''){if(c=='-')f=-f;c=getchar();}
while(c>=''&&c<=''){x=x*+c-'';c=getchar();}
x*=f;
}
using namespace std;
#define N 500010
struct Trie{
ll val[N],ch[N][],sz;
Trie(){
sz=;
memset(ch[],,sizeof(ch[]));
memset(val,,sizeof(val));
}//初始化
ll idx(char c){return c-'a';}
void insert(char *s){
ll u=,len=strlen(s+);
for(ll i=;i<=len;i++){
ll c=idx(s[i]);
if(!ch[u][c]){
memset(ch[sz],,sizeof(ch[sz]));
// val[sz]=0; 这里并不需要这么标记中间信息,因为在初始化那里已经将所有的附加信息初始化为0了
ch[u][c]=sz++;
}
u=ch[u][c];
}
}
ll search(char *s){
ll u=,len=strlen(s+);
for(ll i=;i<=len;i++){
ll c=idx(s[i]);
if(!ch[u][c])return ;
//如果没有这个节点,也就意味着询问的这个字符串不存在,输出WRONG
u=ch[u][c];
}
if(!val[u]){
val[u]=;return ;//这个字符串已经询问过了,标识一下,然后输出OK
}
return ;//这个字符串正确,但不是第一次出现,输出REPEAT
}
}tree;
ll n,m;
int main(){
read(n);char s[];
for(ll i=;i<=n;i++){
scanf("%s",s+);
tree.insert(s);
}
read(m);
for(ll i=;i<=m;i++){
scanf("%s",s+);
ll pd=tree.search(s);
if(pd==)printf("WRONG\n");
else if(pd==)printf("OK\n");
else if(pd==)printf("REPEAT\n");
}
return ;
}

2.UVA11732 "strcmp()" Anyone?(这里给的是洛谷的链接,国内进uva比较慢)

update:2018.5.22

给一点提示:

1.注意要用左儿子右兄弟法储存

2.题目中的“比较次数”说的有点迷,这里阐述一下

相同长度为L的两个单词比较代价为2L-1,除了最后一次外要进行s结束的判断;

单词的比较长度应该为strlen(s)+1,结束符也是比较的一环;

如果两个单词相同,则多计算一次结束判断,比较次数+1。

这里就不给代码了(个人感觉这道题独立思考会更好一些,挺经典的一道题了,更主要是因为我这道题的代码打的比较丑)

至于翻译网上找找就有的

3.[AHOI2005]病毒检测

update:2018.6.2

做法:Trie+dfs/bfs,也可以尝试用dp写

之后会搞一篇这道题的题解,坑先留着

四.总结

上面的题目打过一遍之后基本就可以把Trie掌握了,注:会不定时更新题目

Trie其实不算是多难的数据结构,算是字符串里面一个比较基本的东西吧,然后就是推荐如果学习完Trie可以把hash和kmp也顺便给学了,当然,如果你Trie和kmp都吃透了就可以尝试挑战一下AC自动机了!


转载请注明出处:http://www.cnblogs.com/henry-1202/p/9029822.html

详解Trie的更多相关文章

  1. trie字典树详解及应用

    原文链接    http://www.cnblogs.com/freewater/archive/2012/09/11/2680480.html Trie树详解及其应用   一.知识简介        ...

  2. trie树--详解

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

  3. 转:trie树--详解

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

  4. 字典树(Trie)详解

    详解字典树(Trie) 本篇随笔简单讲解一下信息学奥林匹克竞赛中的较为常用的数据结构--字典树.字典树也叫Trie树.前缀树.顾名思义,它是一种针对字符串进行维护的数据结构.并且,它的用途超级广泛.建 ...

  5. [转] AC自动机详解

    转载自:http://hi.baidu.com/nialv7/item/ce1ce015d44a6ba7feded52d AC自动机详解 AC自动机是用来处理多串匹配问题的,即给你很多串,再给你一篇文 ...

  6. 【转】AC算法详解

    原文转自:http://blog.csdn.net/joylnwang/article/details/6793192 AC算法是Alfred V.Aho(<编译原理>(龙书)的作者),和 ...

  7. 以太坊客户端Geth命令用法-参数详解

    Geth在以太坊智能合约开发中最常用的工具(必备开发工具),一个多用途的命令行工具. 熟悉Geth可以让我们有更好的效率,大家可收藏起来作为Geth命令用法手册. 本文主要是对geth help的翻译 ...

  8. HanLP中人名识别分析详解

    HanLP中人名识别分析详解 在看源码之前,先看几遍论文<基于角色标注的中国人名自动识别研究> 关于命名识别的一些问题,可参考下列一些issue: l ·名字识别的问题 #387 l ·机 ...

  9. HanLP中的人名识别分析详解

    在看源码之前,先看几遍论文<基于角色标注的中国人名自动识别研究> 关于命名识别的一些问题,可参考下列一些issue: u u名字识别的问题 #387 u u机构名识别错误 u u关于层叠H ...

随机推荐

  1. C#之面向对象

    话说三国时期,曹操带领百万大军攻打东吴,大军在长江赤壁驻扎,军船连成一片,眼看就要灭掉东吴,统一天下,曹操大悦,于是晏众文武,在酒席间,曹操诗兴大发,不觉吟道:"喝酒唱歌,人生真爽" ...

  2. cygwin 下安装python MySQLdb

    cygwin 下安装python MySQLdb 1) cygwin 更新 运行 cygwin/setup-x86_64.exe a 输入mysql,选择下面的包安装: libmysqlclient- ...

  3. Hadoop 的 TotalOrderPartitioner

    Partition所处的位置 Partition位置 Partition主要作用就是将map的结果发送到相应的reduce.这就对partition有两个要求: 1)均衡负载,尽量的将工作均匀的分配给 ...

  4. 关于hashCode与equals

    首先我得说明,在我们自己写得类中你可以复写这两个方法,此时从语法的角度来说,他们没关系. 在object中 public native int hashCode(); public boolean e ...

  5. 二分算法C实现

    #include <stdio.h> #include <stdlib.h> #define NR(x) (sizeof(x)/sizeof(x[0])) int Binary ...

  6. Java进阶(十五)Java中设置session的详细解释

    Java中设置session的详细解释 简单通俗的讲session就是象一个临时的容器,用来存放临时的东西.从你登陆开始就保存在session里,当然你可以自己设置它的有效时间和页面,举个简单的例子: ...

  7. android 实现倒影

    首先,文章中出现的Gallery 已经不再适用,替代方法请看我的另一篇文章http://blog.csdn.net/xiangzhihong8/article/details/51120460 不过对 ...

  8. PS 滤镜算法原理——曝光过度

    这个算法的原理,就是将图像反相,然后分别比较原图与反相后的图三个通道的大小,将小的值输出. clc; clear all; Image=imread('4.jpg'); Image=double(Im ...

  9. ZooKeeper客户端事件串行化处理

    为了提升系统的性能,进一步提高系统的吞吐能力,最近公司很多系统都在进行异步化改造.在异步化改造的过程中,肯定会比以前碰到更多的多线程问题,上周就碰到ZooKeeper客户端异步化过程中的一个死锁问题, ...

  10. Android源代码目录组成介绍-android学习之旅(97)

    android的主要源代码组成如下: Kernel:Android Linux 内核2.6 bionic:Android 标准C运行支持库 bootloader:内核加载器参考 build:Andro ...