算法学习笔记(15): Trie(字典树)
Trie树
Trie(字典树)是一种用于实现字符串检索的多叉树。
Trie的每一个节点都可以通过 c
转移到下一层的一个节点。
我们可以看作可以通过某个字符转移到下一个字符串状态,直到转移到最终态为止。这是后话……
我们以插入了字符串 ab
,aa
,b
三个字符串的Trie树为例:
其实一看图就非常清晰了
在上图中,如果我们需要继续插入一个字符串 abc
,那么就只需要新建一个节点即可
思路清晰,那么代码如何实现?
- 首先是插入部分:
struct Node {
int kids[65];
int cnt;
} nodes[N];
#define kids(p, j) nodes[p].kids[j]
#define cnt(p) nodes[p].cnt
void insert(char * s, int len) {
int p = 0;
for (int i = 0; i < len; ++i) {
int j = discrete(s[i]);
if (!kids(p, j)) kids(p, j) = ++usage; // 新建节点
p = kids(p, j);
}
++cnt(p);
}
discrete指的是离散化,例如这里是将
a-z
用0-25
表示
最终的 cnt
表示有几个字符串在当前节点结束。
- 然后是查询部分
我们还是利用类似的思路,一个一个向下走。
例如我们要查询字符串 aba
,那么我们从根节点 0
开始,通过 a
走到 1
节点,通过 b
走到 4
节点,发现没有 a
的子节点,表明没有这个字串,结束寻找。
// 这里是查询这个字符串出现了多少次,为0就是没有出现
int count(char * s, int len) {
int p = 0;
for (int i = 0; i < len; ++i) {
int j = discrete(s[i]);
if (!kids(p, j)) return 0;
p = kids(p, j);
}
return cnt(p);
}
其实主要操作就这两个,我们考虑一下空间和时间复杂度:
时间复杂度很明显是与字符串长度相关的,我们每处理一个字符走一个节点,也就是 \(O(L)\) 的复杂度,那么总的复杂度就是 \(O(NL)\)
至于空间复杂度,每处理一个字符串至多新建 \(L\) 个节点,那么就是 \(O(L)\) ,每一个节点的大小关乎字符串的字符集大小,所以我们认为是 \(O(C)\) 那么总共就是 \(O(NLC)\) ,但是,在实际中,远远达不到此复杂度(除非毒瘤出题人想卡你),例如最初的图,一共 4 个字符串,但是只有 5 个节点……
例题
注意题意,以询问所给作为前缀,求有多少个字符串满足此前缀
那么我们需要魔改一下
insert
函数即可……将++cnt(p)
放入循环中即可还请读者仔细思考
这道题非常的神奇……考虑先建Trie树,如果某一个字符串的字典序比其他任何字符串都大,那么一定不存在为其前缀的字符串。
再考虑字典序,如果使
s
其字典序最大,那么每一个分叉点上,s[i]
比其他所有存在的分叉都要大。如样例:
omm
,moo
,mom
。如果要使
omm
最大那么在第一层上满足o > m
,其他层上没有分叉。如果要使
moo
最大,那么第一层上满足m > o
,第三层上满足o > m
,条件相悖,所以不可行。其他同理。
那么我们如何判断条件相悖?可以借鉴
2-SAT
的思路,通过大于关系建图,如果存在环,那么不可行。判环用拓扑,谁用Tarjan啊
最终,每一个串判断一遍即可。
这道题就是Trie的一种特殊用法。
有点类似线段树的区间标记。
我们考虑改变一个规则对其整个子树都有影响,那么我们考虑什么时候影响抵消?更深的点会阻挡了标记的下传。那么我们记录一下各个点的标记情况,通过类似线段树的方法下传标记即可。
正确性显然。
扩展
Trie树实际上是 AC自动机 和 回文自动机 等自动机的载体,需要经过一点点小变换。
在此不展开叙述,详见我的其他文章。
算法学习笔记(15): Trie(字典树)的更多相关文章
- 道长的算法笔记:Trie字典树
Trie模板 Trie 是一种用于实现字典序快速检索的多叉树结构,Trie 每个节点都有若干的字符指针,若在扫扫描字符串时扫到字符 \(c\),则沿着当前节点 \(c\) 字符指针走向该指针指向的节点 ...
- 算法学习笔记(20): AC自动机
AC自动机 前置知识: 字典树:可以参考我的另一篇文章 算法学习笔记(15): Trie(字典树) KMP:可以参考 KMP - Ricky2007,但是不理解KMP算法并不会对这个算法的理解产生影响 ...
- 萌新笔记——C++里创建 Trie字典树(中文词典)(一)(插入、遍历)
萌新做词典第一篇,做得不好,还请指正,谢谢大佬! 写了一个词典,用到了Trie字典树. 写这个词典的目的,一个是为了压缩一些数据,另一个是为了尝试搜索提示,就像在谷歌搜索的时候,打出某个关键字,会提示 ...
- 算法导论:Trie字典树
1. 概述 Trie树,又称字典树,单词查找树或者前缀树,是一种用于快速检索的多叉树结构,如英文字母的字典树是一个26叉树,数字的字典树是一个10叉树. Trie一词来自retrieve,发音为/tr ...
- 标准Trie字典树学习二:Java实现方式之一
特别声明: 博文主要是学习过程中的知识整理,以便之后的查阅回顾.部分内容来源于网络(如有摘录未标注请指出).内容如有差错,也欢迎指正! 系列文章: 1. 标准Trie字典树学习一:原理解析 2.标准T ...
- SQL反模式学习笔记3 单纯的树
2014-10-11 在树形结构中,实例被称为节点.每个节点都有多个子节点与一个父节点. 最上层的节点叫做根(root)节点,它没有父节点. 最底层的没有子节点的节点叫做叶(leaf). 中间的节点简 ...
- SQL反模式学习笔记15 分组
目标:查询得到每组的max(或者min等其他聚合函数)值,并且得到这个行的其他字段 反模式:引用非分组列 单值规则:跟在Select之后的选择列表中的每一列,对于每个分组来说都必须返回且仅返回一直值. ...
- C++里创建 Trie字典树(中文词典)(一)(插入、遍历)
萌新做词典第一篇,做得不好,还请指正,谢谢大佬! 写了一个词典,用到了Trie字典树. 写这个词典的目的,一个是为了压缩一些数据,另一个是为了尝试搜索提示,就像在谷歌搜索的时候,打出某个关键字,会提示 ...
- Johnson 全源最短路径算法学习笔记
Johnson 全源最短路径算法学习笔记 如果你希望得到带互动的极简文字体验,请点这里 我们来学习johnson Johnson 算法是一种在边加权有向图中找到所有顶点对之间最短路径的方法.它允许一些 ...
- 算法学习笔记(5): 最近公共祖先(LCA)
最近公共祖先(LCA) 目录 最近公共祖先(LCA) 定义 求法 方法一:树上倍增 朴素算法 复杂度分析 方法二:dfs序与ST表 初始化与查询 复杂度分析 方法三:树链剖分 DFS序 性质 重链 重 ...
随机推荐
- 《C# in depth》第5章C#5.0中的更改(十三)——異步枚舉器
一.異步枚舉 异步枚举器(Async Enumerator)是指一种异步迭代器,可以用于处理异步数据源.它允许我们以异步的方式逐个读取数据源中的元素. 在传统的同步枚举器中,当我们遍历一个集合时,程序 ...
- 力扣58(java)-最后一个单词的长度(简单)
题目: 给你一个字符串 s,由若干单词组成,单词前后用一些空格字符隔开.返回字符串中 最后一个 单词的长度. 单词 是指仅由字母组成.不包含任何空格字符的最大子字符串. 示例 1: 输入:s = &q ...
- 基于 Serverless 架构的头像漫画风处理小程序
简介: 当一个程序员想要个漫画风的头像时... 前言 我一直都想要有一个漫画版的头像,奈何手太笨,用了很多软件 "捏不出来",所以就在想着,是否可以基于 AI 实现这样一个功能, ...
- dotnet 已知问题 使用 Directory.EnumerateXXX 方法枚举 C 盘根路径可能错误的问题
在 dotnet 里面,可以使用 Directory.EnumerateXXX 系列方法进行枚举文件或文件夹.在准备枚举驱动器根路径的文件或文件夹时,可能获取到错误的路径.错误的步骤在于传入的是如 C ...
- dotnet 在 UOS 国产系统上使用 Xamarin Forms 创建 xaml 界面的 GTK 应用
在前面几篇博客告诉大家如何部署 GTK 应用,此时的应用是特别弱的,大概只是到拖控件级.尽管和 WinForms 一样也能写出特别强大的应用,但是为了提升一点开发效率,咱开始使用 xaml 神器写界面 ...
- 2019-10-31-ASP.NET-Core-连接-GitLab-与-MatterMost-打造-devops-工具
title author date CreateTime categories ASP.NET Core 连接 GitLab 与 MatterMost 打造 devops 工具 lindexi 201 ...
- 009_原理图中电气互连,Net alias,分页符,总线
009_原理图中电气互连,Net alias,分页符,总线 1.电气互连,就是画线. 2.端口名,适用同一页相连的端口. 3.分页符off page connector,适用于不同页的端口连接. 4. ...
- 使用 @NoRepositoryBean 简化数据库访问
在 Spring Data JPA 应用程序中管理跨多个存储库接口的数据库访问逻辑可能会变得乏味且容易出错.开发人员经常发现自己为常见查询和方法重复代码,从而导致维护挑战和代码冗余.幸运的是,Spri ...
- 使用 Docker 自建一款怀旧游戏之 - 扫雷
1)扫雷 简介 扫雷 是一种经典的单人电脑游戏,最初由微软公司在 1990 年代开发并内置在 Windows 操作系统中.游戏的目标是在一个由方块组成的网格上揭开所有非地雷的方块,而不触发地雷.每个方 ...
- 一个支持Sora模型文本生成视频的Web客户端
大家好,我是 Java陈序员. 最近 Open AI 又火了一把,其新推出的文本生成视频模型 -- Sora,引起了巨大的关注. Sora 目前仅仅只是发布预告视频,还未开放出具体的 API. 今天, ...