【学习笔记】带你从0开始学习 01Trie
01Trie
Section 1:普通 Trie
Section 1.1 什么是 Trie
Trie 树,即字典树,是一种树形结构。典型应用是用于统计和排序大量的字符串前缀来减少查询时间,最大限度地减少无谓的字符串比较。

Section 1.2 如何实现
具体地说,对于每个结点,我们要保存几个信息:
ch[26],保存此字符的下一个字符(\(a\sim z\))的存储地址(没有为 \(0\))。cnt,保存此节点被经过了多少次。
对于整个 Trie 树,我们还要额外保存
Tcnt,为节点数。Endp[],表示的是这个字符串是否以这个下标结尾(如果只是看是否是前缀,则不需要此数组)。
几个操作
insert:往 Trie 树里插入一个字符串。
具体实现:把字符串里的字符扫描一遍,设当前字符为 \(s\),如果 ch[s-'a'] 不等于 \(0\),跳转到 ch[s-'a'] 的存储下标。否则把 ch[s-'a'] 设为树的节点数加一,然后跳转。跳转时把当前节点的 \(cnt\) 加一。
跳到最后把当前节点 \(Endp\) 设为一。
find:查询 Trie 里是否有这个字符串。
具体实现:根据每个字符一个一个跳。
如果跳的时候 \(cnt=0\),说明没有。
如果跳到最后,有,但是 \(Endp_{nowNode}=0\) 即并不是以这个字符结尾的,说明没有。
否则有这个字符串,返回 true。
Section 1.3 代码实现
(只给出基础的插入和查询出现次数)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=3000005;
struct Trie{
int T[N][63],Tsiz,Endp[N];
void init(){
for(int i=0;i<=Tsiz;i++) for(int j=0;j<=62;j++) T[i][j]=0;
for(int i=1;i<=Tsiz;i++) Endp[i]=0;
Tsiz=0;
}
int gethash(char ch){
if(islower(ch)) return ch-'a';
if(isupper(ch)) return ch-'A'+26;
if(isdigit(ch)) return ch-'0'+26+26;
}
void insert(string s){
int rt=0,len=s.length();
for(int i=0;i<len;i++){
int x=gethash(s[i]);
if(!T[rt][x]) T[rt][x]=++Tsiz;
rt=T[rt][x];
}
Endp[rt]++;
}
int find(string s){
int rt=0,len=s.length();
for(int i=0;i<len;i++){
int x=gethash(s[i]);
if(!T[rt][x]) return 0;
rt=T[rt][x];
}
return Endp[rt];
}
}trie;
int T,n,q;
int main(){
trie.init();
cin>>n>>q;
string s;
for(int i=1;i<=n;i++){
cin>>s;
trie.insert(s);
}
for(int i=1;i<=q;i++){
cin>>s;
cout<<trie.find(s)<<'\n';
}
}
Section 1.4 例题实现
(使用以上代码无法通过此题,请写 \(cnt\) 维护前缀)
Section 2:01Trie
Section 2.1 什么是 01Trie?
和普通 Trie 相似,但是每个节点只有两个值:\(0/1\)。
从根节点至下的一条路径保存着一个正整数从高到低的二进制位。
如下图:

中序遍历结果为(忽略根节点): \(00\ 01\ 10\ 11\)
我们会发现几点有趣的性质:
- 01Trie 是一棵二叉树,每个节点的左儿子为 \(0\),右儿子为 \(1\)。
- 从根节点往下,所有左儿子开始的路径值都小于右儿子开始的路径值。
运用这两点性质,我们就可以用 01Trie 造一棵平衡树。
Section 2.2 平衡树
对于每个节点,我们维护两个信息:
siz,维护以当前节点为根节点的子树大小。cnt,维护数字到当前节点为二进制的最后一位的数字个数。
此外,和普通 Trie 一样,我们还要维护树的大小 \(p\)。
几个操作
insert:平衡树的插入操作。首先,给每个经过的结点的 \(siz\) 加一,表示子树节点的个数多了一个。如果当前数字的当前二进制位为 \(0\),就把他放在左儿子,否则放在右儿子。插入到最后一位时给当前节点的 \(cnt\) 加 \(1\)。delete:平衡树的删除操作,与insert几乎一样,只是把最后一位 \(cnt-1\) 就行了。get_rank:查询当前数的排名。从根节点开始往下,如果当前数的当前二进制位为 \(1\),就把排名加上它的左子树的值(性质二)。get_kth:查询排名为 \(k\) 的数。从根节点往下,设它的左子树 \(siz\) 为 \(tmp\),则如果 \(k \le tmp\),则说明排名为 \(k\) 的节点在它的左子树上。否则向右子树查找,并把答案的当前二进制位设为 \(1\)。pre:求当前数的前驱。返回get_kth(get_rank(x))即可。nxt:求当前数的后继。返回get_kth(get_rank(x+1)+1)即可。
Section 2.3 代码实现
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int MAXLOG=24;
const int N=1e7;
class _01trie{
private:
struct node{
int ch[2];
int siz,cnt;
}T[1<<MAXLOG];
int p;
public:
void update(int x,int offset){
int now=0;
for(int i=MAXLOG-1;i>=0;i--){
bool now_bit=(x&(1<<i));
if(T[now].ch[now_bit]==0)
T[now].ch[now_bit]=++p;
now=T[now].ch[now_bit];
T[now].siz+=offset;
}
T[now].cnt+=offset;
}
int get_rank(int x){
int now=0,ans=0;
for(int i=MAXLOG-1;i>=0;i--){
bool now_bit=(x&(1<<i));
if(now_bit==1)
ans+=T[T[now].ch[0]].siz;
now=T[now].ch[now_bit];
if(now==0)
break;
}
return ans;
}
int get_kth(int k){
int now=0,ans=0;
for(int i=MAXLOG-1;i>=0;i--){
int tmp=T[T[now].ch[0]].siz;
if(k<=tmp)
now=T[now].ch[0];
else{
k-=tmp;
now=T[now].ch[1];
ans|=(1<<i);
}
if(now==0)
break;
}
return ans;
}
int pre(int x){
return get_kth(get_rank(x));
}
int nxt(int x){
return get_kth(get_rank(x+1)+1);
}
}Trie;
int n;
int opt,x;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&opt,&x);
if(opt==1) Trie.update(x+N,1);
if(opt==2) Trie.update(x+N,-1);
if(opt==3) printf("%d\n",Trie.get_rank(x+N)+1);
if(opt==4) printf("%d\n",Trie.get_kth(x)-N);
if(opt==5) printf("%d\n",Trie.pre(x+N)-N);
if(opt==6) printf("%d\n",Trie.nxt(x+N)-N);
}
}
Section 2.4 例题
【学习笔记】带你从0开始学习 01Trie的更多相关文章
- 学习笔记︱Nvidia DIGITS网页版深度学习框架——深度学习版SPSS
DIGITS: Deep Learning GPU Training System1,是由英伟达(NVIDIA)公司开发的第一个交互式深度学习GPU训练系统.目的在于整合现有的Deep Learnin ...
- Hadoop源码学习笔记(3) ——初览DataNode及学习线程
Hadoop源码学习笔记(3) ——初览DataNode及学习线程 进入了main函数,我们走出了第一步,接下来看看再怎么走: public class DataNode extends Config ...
- 一起学ASP.NET Core 2.0学习笔记(二): ef core2.0 及mysql provider 、Fluent API相关配置及迁移
不得不说微软的技术迭代还是很快的,上了微软的船就得跟着她走下去,前文一起学ASP.NET Core 2.0学习笔记(一): CentOS下 .net core2 sdk nginx.superviso ...
- Linux简易APR内存池学习笔记(带源码和实例)
先给个内存池的实现代码,里面带有个应用小例子和画的流程图,方便了解运行原理,代码 GCC 编译可用.可以自己上网下APR源码,参考代码下载链接: http://pan.baidu.com/s/1hq6 ...
- NVIDIA DIGITS 学习笔记(NVIDIA DIGITS-2.0 + Ubuntu 14.04 + CUDA 7.0 + cuDNN 7.0 + Caffe 0.13.0)
转自:http://blog.csdn.net/enjoyyl/article/details/47397505?from=timeline&isappinstalled=0#10006-we ...
- [读书笔记]C#学习笔记五: C#3.0自动属性,匿名属性及扩展方法
前言 这一章算是看这本书最大的收获了, Lambda表达式让人用着屡试不爽, C#3.0可谓颠覆了我们的代码编写风格. 因为Lambda所需篇幅挺大, 所以先总结C#3.0智能编译器给我们带来的诸多好 ...
- C#学习笔记四: C#3.0自动属性&匿名属性及扩展方法
前言 这一章算是看这本书最大的收获了, Lambda表达式让人用着屡试不爽, C#3.0可谓颠覆了我们的代码编写风格. 因为Lambda所需篇幅挺大, 所以先总结C#3.0智能编译器给我们带来的诸多好 ...
- JVM 学习笔记 - 带你掌握JVM类加载机制
前言 往期JVM系列: 精美图文带你掌握 JVM 内存布局 本节主要内容: 类的生命周期 类加载阶段描述 数组类和非数组类在加载阶段的差别 父子类初始化顺序 接口的初始化 JVM如何处理 多线程同时初 ...
- [读书笔记]C#学习笔记七: C#4.0中微小改动-可选参数,泛型的可变性
前言 下面就开始总结C#4.0的一些变化了, 也是这本书中最后的一点内容了, 这一部分终于要更新完了. 同时感觉再来读第二遍也有不一样的收获. 今天很嗨的是武汉下雪了,明天周六,一切都是这么美好.哈哈 ...
随机推荐
- Blazor Hybrid / MAUI 简介和实战
1. Blazor Blazor 是一个使用 .NET 生成交互式客户端 Web UI 的框架: 使用 C# 代替 JavaScript 来创建信息丰富的交互式 UI. 共享使用 .NET 编写的服务 ...
- 使用 AgileConfig 动态配置 NLog
NLog 是我们在 .NET 领域使用非常广泛的日志组件.它默认使用 xml 来维护它的配置.最近有几个同学问我当使用 AgileConfig 的时候如何配置 NLog .因为 AgileConfig ...
- python封装发送邮件类
import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart i ...
- 解决python 导入selenium 库后自动化运行成功但是报错问题
本章节开始进入自动化的基础教学了,首先我们要对我们的工具有一定的熟练使用程度,做自动化常用的工具一个是搭建 RobotFramework自动化框架,另外一个便是我们最常用的python 工作原理是比较 ...
- 2021.05.04【NOIP提高B组】模拟 总结
T1 题目大意, \(S_{i,j}=\sum_{k=i}^j a_k\) ,求 \(ans=\min\{ S_{i,j}\mod P|S_{i,j}\mod P\ge K \}\) 其中 \(i\l ...
- Jmeter(五十三) - 从入门到精通高级篇 - 懒人教你在Linux系统中安装Jmeter(详解教程)
1.简介 我们绝大多数使用的都是Windows操作系统,因此在Windows系统上安装JMeter已经成了家常便饭,而且安装也相对简单,但是服务器为了安全.灵活小巧,特别是前几年的勒索病毒,现在绝大多 ...
- 接口偶尔超时,竟又是JVM停顿的锅!
原创:扣钉日记(微信公众号ID:codelogs),欢迎分享,转载请保留出处. 简介 继上次我们JVM停顿十几秒的问题解决后,我们系统终于稳定了,再也不会无故重启了! 这是之前的文章:耗时几个月,终于 ...
- react native 0.6x 在创建项目时,CocoaPods 的依赖安装步骤卡解决方案
前言 你需要做两件事 gem换源 pod repo 换源 实战 如果你已经成功安装了CocoaPods.那么这里你需要卸载它.gem换源1. 卸载CocoaPods 查看gem安装的东西 gem li ...
- 关于Vue移动端框架(Muse-UI)的使用(说明书,针对不愿看文档的童鞋)
一.安装 1.npm安装 npm i muse-ui -S 或者 CDN安装 <link rel="stylesheet" href="https://unpkg. ...
- ansible对文件内容操作
ansible lineinfile 简介 lineinfile该模块是操作文件中的每一行内容,他是按照行为单位的,和下面的replace模块并不冲突. 修改匹配行,如果不存在就会添加 tasks: ...