字典树Trie详解
哈喽大家好,我是 doooge ,今天给大家带来 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 树就建好了,建树的时间复杂度为:
\]
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\) 次树的复杂度为:
\]
是不是发现建树和搜树差不多?于是就有了一些边建边搜的题。
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:
7.闲话
下一篇文章将讲 Trie的变种:01-Trie和Trie的合并,还有可持久化Trie,期待一下!
蒟蒻不才,膜拜大佬。如果文章错了字或代码,请在评论区@我。
字典树Trie详解的更多相关文章
- 字典树(Trie)详解
详解字典树(Trie) 本篇随笔简单讲解一下信息学奥林匹克竞赛中的较为常用的数据结构--字典树.字典树也叫Trie树.前缀树.顾名思义,它是一种针对字符串进行维护的数据结构.并且,它的用途超级广泛.建 ...
- [POJ] #1002# 487-3279 : 桶排序/字典树(Trie树)/快速排序
一. 题目 487-3279 Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 274040 Accepted: 48891 ...
- 『字典树 trie』
字典树 (trie) 字典树,又名\(trie\)树,是一种用于实现字符串快速检索的树形数据结构.核心思想为利用若干字符串的公共前缀来节约储存空间以及实现快速检索. \(trie\)树可以在\(O(( ...
- 字典树trie学习
字典树trie的思想就是利用节点来记录单词,这样重复的单词可以很快速统计,单词也可以快速的索引.缺点是内存消耗大 http://blog.csdn.net/chenleixing/article/de ...
- 『动善时』JMeter基础 — 32、JMeter察看结果树组件详解
目录 1.察看结果树介绍 2.察看结果树界面详解 3.察看结果树的其他功能 (1)将数据写入文件中 (2)Search功能 (3)Scroll automatically选项 4.总结 1.察看结果树 ...
- Trie树(字典树,单词查找树)详解+题目
什么是字典树? 叫前缀树更容易理解 字典树的样子 Trie又被称为前缀树.字典树,所以当然是一棵树.上面这棵Trie树包含的字符串集合是{in, inn, int, tea, ten, to}.每个节 ...
- 字典树trie的学习与练习题
博客详解: http://www.cnblogs.com/huangxincheng/archive/2012/11/25/2788268.html http://eriol.iteye.com/bl ...
- 字典树(Trie Tree)
在图示中,键标注在节点中,值标注在节点之下.每一个完整的英文单词对应一个特定的整数.Trie 可以看作是一个确定有限状态自动机,尽管边上的符号一般是隐含在分支的顺序中的.键不需要被显式地保存在节点中. ...
- 字典树(Trie树)实现与应用
一.概述 1.基本概念 字典树,又称为单词查找树,Tire数,是一种树形结构,它是一种哈希树的变种. 2.基本性质 根节点不包含字符,除根节点外的每一个子节点都包含一个字符 从根节点到某一节点.路径上 ...
- 字典树(Trie树)的实现及应用
>>字典树的概念 Trie树,又称字典树,单词查找树或者前缀树,是一种用于快速检索的多叉树结构,如英文字母的字典树是一个26叉树,数字的字典树是一个10叉树.与二叉查找树不同,Trie树的 ...
随机推荐
- RESTful的连接时间超时时间设定
dsResrful的连接方式时,如何设定timeout呢? DSRestConnection.HTTP.ConnectTimeout := 5000; 就这么简单.因为封装的indy的TidHTTP. ...
- 记一个.NET AOT交叉编译时的坑
记一个.NET AOT交叉编译时的坑 背景: 使用.NET9开发的Avalonia项目需要部署到Linux-arm64 踩坑: 根据官方AOT交叉编译文档配置后执行打包 dotnet publish ...
- 解决 Maven 打包项目中 Excel 文件乱码问题
在 Java 项目开发过程中,我们常常会使用 Maven 来管理项目依赖和进行项目打包.当涉及到使用 Freemarker 导出 Excel 文件时,不少开发者可能会遇到一个让人头疼的问题 --Exc ...
- AoP的相关术语
一.Joinpoint(连接点): 所谓连接点是指那些被拦截到的点.在 spring 中,这些所谓的点指就是方法,因为 spring 只支持方法类型的连接点. 二.Pointcut(切入点): 所谓切 ...
- java基础之Scanner类、 Random类
一.使用Scanner类,完成接收键盘录入数据 格式: Scanner sc = new Scanner(System.in) sc.nextInt(); 二.获取1-n之间的随机数 格式: Rand ...
- 如何使用Git命令将代码上传到GitHub
1. 首先在我们的计算机上创建文件夹:例如取名:test 2. 进入test文件夹后点击鼠标右键,选择打开Git Bash. 3. 将我们创建的文件夹初始化为Git仓库:git init 4. 将要上 ...
- 通用型产品发布解决方案(基于分布式微服务技术栈:SpringBoot+SpringCloud+Spring CloudAlibaba+Vue+ElementUI+MyBatis-Plus+MySQL+Git+Maven+Linux+Docker+Nginx - 《01》
通用型产品发布解决方案(基于分布式微服务技术栈:SpringBoot+SpringCloud+Spring CloudAlibaba+Vue+ElementUI+MyBatis-Plus+MySQL+ ...
- 没几个人需要了解的JDK知识,我却花了3天时间研究
目前国内发布自己JDK版本的几家公司: 腾讯和阿里是因为有Java应用和云业务,所以在优化发布自己的版本 华为也是因为Java应用和云业务,不过因为还有服务器业务,所以还有Java课题的跑分需求,如S ...
- thinkphp mysql 使用IN 条件
今天使用thinkphp whrere in条件查询 数据库是 ,我需要搜索入参 110000 一个字段 ,但是thinkphp 为了效率直接把 in条件转成 = 解决方法 FIND_IN ...
- php获取前一天,前一个月,前半年,前一年的时间戳
#获取前一小时strtotime("-1 hour") #获取前一天strtotime("-1 day") #获取前一周strtotime("-1 w ...