01Trie

Section 1:普通 Trie

Section 1.1 什么是 Trie

Trie 树,即字典树,是一种树形结构。典型应用是用于统计和排序大量的字符串前缀来减少查询时间,最大限度地减少无谓的字符串比较。


Section 1.2 如何实现

具体地说,对于每个结点,我们要保存几个信息:

  • ch[26] ,保存此字符的下一个字符(\(a\sim z\))的存储地址(没有为 \(0\))。
  • cnt ,保存此节点被经过了多少次。

对于整个 Trie 树,我们还要额外保存

  • Tcnt,为节点数。
  • Endp[],表示的是这个字符串是否以这个下标结尾(如果只是看是否是前缀,则不需要此数组)。

几个操作

  1. insert:往 Trie 树里插入一个字符串。

具体实现:把字符串里的字符扫描一遍,设当前字符为 \(s\),如果 ch[s-'a'] 不等于 \(0\),跳转到 ch[s-'a'] 的存储下标。否则把 ch[s-'a'] 设为树的节点数加一,然后跳转。跳转时把当前节点的 \(cnt\) 加一。

跳到最后把当前节点 \(Endp\) 设为一。

  1. 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 例题实现

【洛谷模板题 Link】

(使用以上代码无法通过此题,请写 \(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\)。


几个操作

  1. insert:平衡树的插入操作。首先,给每个经过的结点的 \(siz\) 加一,表示子树节点的个数多了一个。如果当前数字的当前二进制位为 \(0\),就把他放在左儿子,否则放在右儿子。插入到最后一位时给当前节点的 \(cnt\) 加 \(1\)。

  2. delete:平衡树的删除操作,与 insert 几乎一样,只是把最后一位 \(cnt-1\) 就行了。

  3. get_rank:查询当前数的排名。从根节点开始往下,如果当前数的当前二进制位为 \(1\),就把排名加上它的左子树的值(性质二)。

  4. get_kth:查询排名为 \(k\) 的数。从根节点往下,设它的左子树 \(siz\) 为 \(tmp\),则如果 \(k \le tmp\),则说明排名为 \(k\) 的节点在它的左子树上。否则向右子树查找,并把答案的当前二进制位设为 \(1\)。

  5. pre:求当前数的前驱。返回 get_kth(get_rank(x)) 即可。

  6. 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 例题

【洛谷模板题 Link】

【学习笔记】带你从0开始学习 01Trie的更多相关文章

  1. 学习笔记︱Nvidia DIGITS网页版深度学习框架——深度学习版SPSS

    DIGITS: Deep Learning GPU Training System1,是由英伟达(NVIDIA)公司开发的第一个交互式深度学习GPU训练系统.目的在于整合现有的Deep Learnin ...

  2. Hadoop源码学习笔记(3) ——初览DataNode及学习线程

    Hadoop源码学习笔记(3) ——初览DataNode及学习线程 进入了main函数,我们走出了第一步,接下来看看再怎么走: public class DataNode extends Config ...

  3. 一起学ASP.NET Core 2.0学习笔记(二): ef core2.0 及mysql provider 、Fluent API相关配置及迁移

    不得不说微软的技术迭代还是很快的,上了微软的船就得跟着她走下去,前文一起学ASP.NET Core 2.0学习笔记(一): CentOS下 .net core2 sdk nginx.superviso ...

  4. Linux简易APR内存池学习笔记(带源码和实例)

    先给个内存池的实现代码,里面带有个应用小例子和画的流程图,方便了解运行原理,代码 GCC 编译可用.可以自己上网下APR源码,参考代码下载链接: http://pan.baidu.com/s/1hq6 ...

  5. 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 ...

  6. [读书笔记]C#学习笔记五: C#3.0自动属性,匿名属性及扩展方法

    前言 这一章算是看这本书最大的收获了, Lambda表达式让人用着屡试不爽, C#3.0可谓颠覆了我们的代码编写风格. 因为Lambda所需篇幅挺大, 所以先总结C#3.0智能编译器给我们带来的诸多好 ...

  7. C#学习笔记四: C#3.0自动属性&匿名属性及扩展方法

    前言 这一章算是看这本书最大的收获了, Lambda表达式让人用着屡试不爽, C#3.0可谓颠覆了我们的代码编写风格. 因为Lambda所需篇幅挺大, 所以先总结C#3.0智能编译器给我们带来的诸多好 ...

  8. JVM 学习笔记 - 带你掌握JVM类加载机制

    前言 往期JVM系列: 精美图文带你掌握 JVM 内存布局 本节主要内容: 类的生命周期 类加载阶段描述 数组类和非数组类在加载阶段的差别 父子类初始化顺序 接口的初始化 JVM如何处理 多线程同时初 ...

  9. [读书笔记]C#学习笔记七: C#4.0中微小改动-可选参数,泛型的可变性

    前言 下面就开始总结C#4.0的一些变化了, 也是这本书中最后的一点内容了, 这一部分终于要更新完了. 同时感觉再来读第二遍也有不一样的收获. 今天很嗨的是武汉下雪了,明天周六,一切都是这么美好.哈哈 ...

随机推荐

  1. Blazor Hybrid / MAUI 简介和实战

    1. Blazor Blazor 是一个使用 .NET 生成交互式客户端 Web UI 的框架: 使用 C# 代替 JavaScript 来创建信息丰富的交互式 UI. 共享使用 .NET 编写的服务 ...

  2. 使用 AgileConfig 动态配置 NLog

    NLog 是我们在 .NET 领域使用非常广泛的日志组件.它默认使用 xml 来维护它的配置.最近有几个同学问我当使用 AgileConfig 的时候如何配置 NLog .因为 AgileConfig ...

  3. python封装发送邮件类

    import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart i ...

  4. 解决python 导入selenium 库后自动化运行成功但是报错问题

    本章节开始进入自动化的基础教学了,首先我们要对我们的工具有一定的熟练使用程度,做自动化常用的工具一个是搭建 RobotFramework自动化框架,另外一个便是我们最常用的python 工作原理是比较 ...

  5. 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 ...

  6. Jmeter(五十三) - 从入门到精通高级篇 - 懒人教你在Linux系统中安装Jmeter(详解教程)

    1.简介 我们绝大多数使用的都是Windows操作系统,因此在Windows系统上安装JMeter已经成了家常便饭,而且安装也相对简单,但是服务器为了安全.灵活小巧,特别是前几年的勒索病毒,现在绝大多 ...

  7. 接口偶尔超时,竟又是JVM停顿的锅!

    原创:扣钉日记(微信公众号ID:codelogs),欢迎分享,转载请保留出处. 简介 继上次我们JVM停顿十几秒的问题解决后,我们系统终于稳定了,再也不会无故重启了! 这是之前的文章:耗时几个月,终于 ...

  8. react native 0.6x 在创建项目时,CocoaPods 的依赖安装步骤卡解决方案

    前言 你需要做两件事 gem换源 pod repo 换源 实战 如果你已经成功安装了CocoaPods.那么这里你需要卸载它.gem换源1. 卸载CocoaPods 查看gem安装的东西 gem li ...

  9. 关于Vue移动端框架(Muse-UI)的使用(说明书,针对不愿看文档的童鞋)

    一.安装 1.npm安装 npm i muse-ui -S 或者 CDN安装 <link rel="stylesheet" href="https://unpkg. ...

  10. ansible对文件内容操作

    ansible lineinfile 简介 lineinfile该模块是操作文件中的每一行内容,他是按照行为单位的,和下面的replace模块并不冲突. 修改匹配行,如果不存在就会添加 tasks: ...