哈喽大家好,我是 doooge ,今天给大家带来 Trie 的详解。

\[\Huge \texttt{字典树 Trie 详解}
\]

1.Trie是什么?

Trie 也叫字典树,前缀树,其本质就是一棵字符树。它也是 AC自动机 的一部分。

这是 Trie 的 oiwiki介绍

Trie 通常用来解决查找一个字符串在某个集合里是否出现过,也可以排序,它的时间复杂度很优秀,\(O(|s|)\) 就可以做到一次查询(\(|s|\) 表示 \(s\) 的长度)。

2.Trie的创建

如果需要在这棵树中查询,就必须要创建这棵树。如果我们要存储 \(ant,bag,bat,cat,car\) 这五个字符串,那么这棵树长这样子:

在存储的过程中,我们不必存储每个节点的信息,只要记录它的下一步能到哪里去就行了。在没有字符串存进去的时候,该树只有一个 root 节点

比如说我们第一个要把 \(bag\) 存进去时,此时这棵树只有一个 root 节点,我们需要建立一条边 \(b\),将 \(b\) 节点存进去。之后我们需要建立一条边 \(a\),将 \(ba\) 节点存进去,以此类推,直到存到 \(bag\) 节点为止。

再比如说,我们第一个要把 \(bat\) 存进去时,此时这棵树已经有了 \(b,ba\) 节点,我们不需要建立一条新边,只要往下搜即可。到了 \(ba\) 节点,因为没有 \(bat\) 节点,所以得建立一条边 \(t\),把 \(bat\) 节点存上去。

当然,当一些题目需要询问存进去了哪些串时,需要加一个标记或者计数数组(这取决于该题是否要给它们计数),当存完了时打上标记即可。

这样,一棵 Trie 树就建好了,建树的时间复杂度为:

\[O(\sum_{i=1}^{n} |s_i|)
\]

3.Trie的查询

Trie的查询很好懂。

比如说刚才图片中的树,我们要查询 \(cat\) 是否在树中出现,就只要变搜边看即可。我们得从 root 节点搜起,用 \(pos\) 变量记录搜到哪了,\(pos\) 初始为 \(0\),也就是 root 节点的编号

可以发现,root 节点有一条边连着 \(c\) 节点,因为 \(cat\) 的第 \(1\) 项为 \(c\),那么就将 \(pos\) 设为 \(c\) 节点的编号,继续搜下去。\(c\) 节点有一条边连着 \(a\) 节点,因为 \(cat\) 的第 \(2\) 项为 \(a\),那么就将 \(pos\) 设为 \(a\) 节点的编号,继续搜下去。

以此类推,直到我们搜完 \(cat\) 这个字符串。可以发现,该字符串确实存在,我们就搜完了。

再比如我们要搜索 \(can\) 这个字符串,跟 \(cat\) 差不多,但当我们搜索到 \(ca\) 这个节点时,因为这个节点并没有 \(t\) 这条边,所以搜不到,返回 \(-1\)。当发现搜不到时,返回 -1

搜 \(n\) 次树的复杂度为:

\[O(\sum_{i=1}^{n} |s_i|)
\]

是不是发现建树和搜树差不多?于是就有了一些边建边搜的题。

4.Trie的代码实现

相信你如果读懂了 Trie 建树搜树的过程,一定能自己手打了把。当然不会的也没关系,看这里:

建树:

void build(string s){
int pos=0,len=s.size();
for(int i=0;i<len;i++){
if(!nxt[pos][s[i]-'a']){
nxt[pos][s[i]-'a']=++cnt;
}
pos=nxt[pos][s[i]-'a'];
}
f[pos]=true;
}

搜树:

int find(string s){
int pos=0,len=s.size();
for(int i=0;i<len;i++){
if(!nxt[pos][s[i]-'a'])return -1;
pos=nxt[pos][s[i]-'a'];
}
if(!f[pos])return -1;
return 1;
}

5.Trie的例题

例题1:于是他错误的点名开始了

题目大意:给你 \(n\) 个字符串组成的一个集合,\(m\) 次询问,每次询问给出一个字符串,问你该串是否在集合里出现过,若没出现过,输出 WRONG。如果之前询问点过名,输出 REPEAT。否则输出 OK

虽然这题用 map 也可以 AC,但是这也是一道非常适合 Trie 的板子题。

思路:我们维护一个前缀树,再用一个标记数来统计是否出现过就可以了。

代码:

#include<bits/stdc++.h>
#define code return
#define by 0
#define doooge ;
using namespace std;
int nxt[500010][30],cnt;
bool f[500010],f2[500010];//判断是否出现过
void build(string s){
int pos=0,len=s.size();
for(int i=0;i<len;i++){
if(!nxt[pos][s[i]-'a']){
nxt[pos][s[i]-'a']=++cnt;
}
pos=nxt[pos][s[i]-'a'];
}
f[pos]=true;
}
int find(string s){
int pos=0,len=s.size();
for(int i=0;i<len;i++){
if(!nxt[pos][s[i]-'a'])return -1;
pos=nxt[pos][s[i]-'a'];
}
if(!f[pos])return -1;
if(f2[pos])return 0;
f2[pos]=true;
return 1;
}
int main(){
int n,m;
cin>>n;
for(int i=1;i<=n;i++){
string s;
cin>>s;
build(s);
}
cin>>m;
while(m--){
string s;
cin>>s;
int res=find(s);
if(res==-1)cout<<"WRONG\n";
else if(res==0)cout<<"REPEAT\n";
else cout<<"OK\n";
}
code by doooge
}

例题2:【模板】字典树

思路:这题与我们将的 Trie 有所不同的是他需要计数有多少个子串,这时我们就可以用一个计数数组来代替标记数组就可以了。注意判断大小写。

注意初始化别用 memset,特别慢。

代码:

#include<bits/stdc++.h>
#define code return
#define by 0
#define doooge ;
using namespace std;
int nxt[3000010][70],cnt;
int sum[3000010];
int get(char c){
if(c>='0'&&c<='9')return 52+c-'0';
return (c<'a'?26+c-'A':c-'a');
}
void build(string s){
int pos=0,len=s.size();
for(int i=0;i<len;i++){
if(!nxt[pos][get(s[i])]){
nxt[pos][get(s[i])]=++cnt;
}
pos=nxt[pos][get(s[i])];
sum[pos]++;
}
}
int find(string s){
int pos=0,len=s.size();
for(int i=0;i<len;i++){
if(!nxt[pos][get(s[i])])return 0;
pos=nxt[pos][get(s[i])];
}
return sum[pos];
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin>>t;
while(t--){
for(int i=0;i<=cnt;i++){
for(int j=0;j<=66;j++)nxt[i][j]=0;
}
for(int i=1;i<=cnt;i++)sum[i]=0;
cnt=0;
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
string s;
cin>>s;
build(s);
}
while(m--){
string s;
cin>>s;
cout<<find(s)<<'\n';
}
}
code by doooge
}

6.作业

作业T1:

阅读理解,难度:\(2■■□□□\),虽然 map 也可以做,但是这题也算一道 Trie 板子题。

作业T2:

前缀统计,难度:\(3■■■□□\),跟例题2的思想差不多。

作业T3:

会议座位,难度:\(5■■■■■\),需要求逆序对

7.闲话

下一篇文章将讲 Trie的变种:01-Trie和Trie的合并,还有可持久化Trie,期待一下!

蒟蒻不才,膜拜大佬。如果文章错了字或代码,请在评论区@我。

字典树Trie详解的更多相关文章

  1. 字典树(Trie)详解

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

  2. [POJ] #1002# 487-3279 : 桶排序/字典树(Trie树)/快速排序

    一. 题目 487-3279 Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 274040   Accepted: 48891 ...

  3. 『字典树 trie』

    字典树 (trie) 字典树,又名\(trie\)树,是一种用于实现字符串快速检索的树形数据结构.核心思想为利用若干字符串的公共前缀来节约储存空间以及实现快速检索. \(trie\)树可以在\(O(( ...

  4. 字典树trie学习

    字典树trie的思想就是利用节点来记录单词,这样重复的单词可以很快速统计,单词也可以快速的索引.缺点是内存消耗大 http://blog.csdn.net/chenleixing/article/de ...

  5. 『动善时』JMeter基础 — 32、JMeter察看结果树组件详解

    目录 1.察看结果树介绍 2.察看结果树界面详解 3.察看结果树的其他功能 (1)将数据写入文件中 (2)Search功能 (3)Scroll automatically选项 4.总结 1.察看结果树 ...

  6. Trie树(字典树,单词查找树)详解+题目

    什么是字典树? 叫前缀树更容易理解 字典树的样子 Trie又被称为前缀树.字典树,所以当然是一棵树.上面这棵Trie树包含的字符串集合是{in, inn, int, tea, ten, to}.每个节 ...

  7. 字典树trie的学习与练习题

    博客详解: http://www.cnblogs.com/huangxincheng/archive/2012/11/25/2788268.html http://eriol.iteye.com/bl ...

  8. 字典树(Trie Tree)

    在图示中,键标注在节点中,值标注在节点之下.每一个完整的英文单词对应一个特定的整数.Trie 可以看作是一个确定有限状态自动机,尽管边上的符号一般是隐含在分支的顺序中的.键不需要被显式地保存在节点中. ...

  9. 字典树(Trie树)实现与应用

    一.概述 1.基本概念 字典树,又称为单词查找树,Tire数,是一种树形结构,它是一种哈希树的变种. 2.基本性质 根节点不包含字符,除根节点外的每一个子节点都包含一个字符 从根节点到某一节点.路径上 ...

  10. 字典树(Trie树)的实现及应用

    >>字典树的概念 Trie树,又称字典树,单词查找树或者前缀树,是一种用于快速检索的多叉树结构,如英文字母的字典树是一个26叉树,数字的字典树是一个10叉树.与二叉查找树不同,Trie树的 ...

随机推荐

  1. 关于ASCII码的一些信息(转载自https://blog.csdn.net/na_tion/article/details/50148883)

    ASCII码分基本表(128个字符,从00000000到01111111).扩展表(256个字符,从00000000到11111111)和压缩表(64个字符),我们经常用的是128个的基本表,而在一些 ...

  2. 【Ubuntu】在Ubuntu上安装IDEA

    [Ubuntu]在Ubuntu上安装IDEA 零.前言 最近换了Ubuntu系统,但是还得是要写代码,这样就不可避免地用到IDEA,接下来介绍一下如何在Ubuntu上安装IDEA. 壹.下载 这一步应 ...

  3. 修显示器led屏幕能亮但是显示异常

    用电吹风热风大风   对着显示器的  ' led 区域 '  吹十分钟 吹显示器线插口 电源线 插口 机箱    断电吹  // 温度挺高  还得吹显卡接口 线也要换新的 插口需要用线的接口 打磨金属 ...

  4. Mono GC

    1.虽然是stw但mark阶段可以concurrent 2.并行mark就需要写屏障 3.unity的gc也不是扫描整个堆内存 https://schani.wordpress.com/2012/12 ...

  5. .NET 阻止关机机制以及关机前执行业务

    本文主要介绍Windows在关闭时,如何正确.可靠的阻止系统关机以及关机前执行相应业务.因有一些场景需要在关机/重启前执行业务逻辑,确保下次开机时数据的一致性以及可靠性. 以下是实现这一需求的几种方法 ...

  6. Redis底层数据结构-quicklist、listpack

    quicklist 在 Redis 3.0 之前,List 对象的底层数据结构是双向链表或者压缩列表.然后在 Redis 3.2 的时候,List 对象的底层改由 quicklist 数据结构实现. ...

  7. redis的作用:高性能和高并发

    一.高性能 假设这么个场景,你有个操作,一个请求过来,吭哧吭哧你各种乱七八糟操作mysql,半天查出来一个结果,耗时600ms.但是这个结果可能接下来几个小时都不会变了,或者变了也可以不用立即反馈给用 ...

  8. SVN统计时间段内代码修改行数

    1.本地安装svn客户端(方法自行百度) 注:安装时记得勾选命令行工具 若原安装未勾选,可再次启动安装文件: 选中Next即可: 环境变量记得配置svn路径(bin)(方法自行百度) cmd运行命令 ...

  9. windows切换nodejs版本

    卸载之前的nodejs 第一步:下载nvm并安装 (推荐使用nvm-setup.zip) https://github.com/coreybutler/nvm-windows/releases 第二步 ...

  10. 解决微信二维码接口接口返回:errcode\":47001,\"errmsg\":\"data format error rid: xxx和处理返回的buffer的问题

    data format error rid问题: 在php中使用curl调用微信二维码生成接口getwxacodeunlimit时得到错误响应信息: errcode\":47001,\&qu ...