当前位置: 首页 > article >正文

字典树与01trie

字典树简介

当我们通过字典查一个字或单词的时候,我们会通过前缀或关键字的来快速定位一个字的位置,进行快速查找。

字典树就是类似字典中索引表的一种数据结构,能够帮助我们快速定位一个字符串的位置。

字典树是一种存储字符串的数据结构,除了根节点,每个节点可以存储一个字符,从根节点到树上某一结点的路径代表一个字符串

在向字典树中添加字符串的时候,如果一个字符串在某个节点结束,我们可以给这个节点打上一个标记。这样在后续访问时就可以知道到此节点为止为以一个完成的字符串。

const int N=1e5+10;
//第一维表示节点标号,第二维表示该节点到下一节点
int nxt[N][26];
//标记数组
bool isend[N];
//根节点和树的元素个数
int root=0,cnt=0;

字典树的插入

void insert(char s[],int len)
{int now=0;for(int i=1;i<=len;i++){int x=s[i]-'a';if(!nxt[now][x])nxt[now][x]=++cnt;//当原树中不存在这一条路径时,添加节点,创建路径now=nxt[now][x];}isend[now]=true;
}

 字典树的查找

bool search(char s[],int len)
{int now=0;for(int i=1;i<=len;i++){int x=s[i]-'a';if(!nxt[now][x])return false;now=nxt[now][x];}return isend[now];
}

 字典树的删除的情况比较少见

这时候我们要记录每个数节点的子树大小,这里的子树大小是指在这个节点下有几个真实存在的字符串。

对于要删除的字符串,先用查找字符串的方式找到该字符串在字典树中的最后一个节点,删除标记,然后回溯整条路径,如果路径上节点的子树大小为零,就可以删除这个节点

//删除操作需要有一个e数组
//e[x],表示的是经过x节点有几个字符串
//在删除操作时,需要将该字符串经过的路径e[x]--
//当发现该节点e[x]为1时,就说明只有这一条字符串经过该路径,将此节点删除,就将该字符串成功删除
int e[N]
void insert(char s[],int len)
{int now=0;e[now]++;for(int i=1;i<=len;i++){int x=s[i]-'a';if(!nxt[now][x])nxt[now][x]=++cnt;now=nxt[now][x];e[now]++;}isend[now]=true;
}
void remove(char s[],int len)
{int now=0;e[now]--;for(int i=1;i<=len;i++){int x=s[i]-'a';int tmp=nxt[now][x];if(e[tmp]==1)e[now][x]=0;//删除操作now=tmp;e[tmp]--;}isend[now]=false;
}

写一道例题蓝桥3755小蓝的神秘图书馆

 

 

ac代码如下

#include<iostream>
using namespace std;
using ll = long long;
const int N = 5e5 + 5;
int nxt[N][26];
ll e[N];
int cnt = 0;
int main()
{ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);int n, m; cin >> n >> m;for (int i = 1; i <= n; i++){string str; cin >> str;int len = str.length();int now = 0;e[now]++;for (int j = 0; j < len; j++){int x = str[j] - 'a';if (!nxt[now][x]){nxt[now][x] = ++cnt;}now = nxt[now][x];++e[now];}}while (m--){string str; cin >> str;int len = str.length();int now = 0;bool flag = true;for (int j = 0; j < len && flag; j++){int x = str[j] - 'a';if (!nxt[now][x]){flag = false;}else now = nxt[now][x];}if (flag){cout <<e[now] << endl;}else cout << 0 << endl;}return 0;
}

 字典树除了可以对字符串进行前缀判定,还可以对字符串进行排序

原理就是既然字典树是一颗树,那我们就可以以遍历树的方式来遍历字典树,字典树的每一节点都有26条向下的边(当然大部分边是不存在的)我们优先往小的字典序遍历,进行一次dfs就可以对所有的字符串进行排序了

上例题

 代码如下

#include<iostream>
#include<vector>
#include<map>
#include<string>
#include<algorithm>
#include<queue>
#include<cstring>
#include<stack>
#include<array>
#include<cmath>
#include<set>
#include<unordered_set>
using namespace std;
using ll = long long;
const ll inf = 0x3f3f3f3f3f3f3f3fll;
const int N = 1e5 + 10;
int nxt[N][26];
int cnt = 0,n;
bool isend[N];
int str[30];
void dfs(int x, int deep)
{if (isend[x]){for (int i = 1; i <= deep; i++)cout << (char)(str[i]+'a');cout << endl;}for (int i = 0; i < 26; i++){if (nxt[x][i]){str[deep + 1] = i;dfs(nxt[x][i], deep + 1);}}
}
int main()
{ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);cin >> n;for (int i = 1; i <= n; i++){string s; cin >> s;int len = s.length();int now = 0;for (int j = 0; j < len; j++){int x = s[j] - 'a';if (!nxt[now][x])nxt[now][x] = ++cnt;now = nxt[now][x];}isend[now] = true;}dfs(0, 0);return 0;
}

最后可以用字典树来求解最长公共前缀问题

 

以树的方式来看这个问题,求任意两个字符串的最长公共前缀,其实就是求解两个节点的最近公共祖先,那就得到了最长公共前缀

求解LCA问题,依旧可以使用倍增的思想

代码如下

#include<iostream>
#include<vector>
#include<map>
#include<string>
#include<algorithm>
#include<queue>
#include<cstring>
#include<stack>
#include<array>
#include<cmath>
#include<set>
#include<unordered_set>
using namespace std;
using ll = long long;
const ll inf = 0x3f3f3f3f3f3f3f3fll;
const int N = 1e5 + 10;
int nxt[N][26];
int cnt = 0,n,m;
int ed[N],fa[N][30];
int d[N];
int main()
{ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);cin >> n;for (int i = 1; i <= n; i++){string s; cin >> s;int len = s.length();int now = 0;for (int j = 0; j < len; j++){int x = s[j] - 'a';if (!nxt[now][x]){nxt[now][x] = ++cnt;fa[cnt][0] = now;d[cnt] = d[now] + 1;//记录深度}now = nxt[now][x];}ed[i] = now;//记录每一个字符串的结束节点}//倍增求LCAfor (int i = 1; i <= 20; i++){for (int j = 1; j <= cnt; j++){if (fa[j][i - 1]){fa[j][i] = fa[fa[j][i - 1]][i - 1];}}}cin >> m;while (m--){int x, y; cin >> x >> y;x = ed[x], y = ed[y];if (d[x] < d[y])swap(x, y);int z = d[x] - d[y];for (int i = 0; i <= 20&&z; i++, z >>= 1){if (z & 1)x = fa[x][i];}if (x == y){cout << d[x] << endl;continue;}for (int i = 20; i >= 0; i--){if (fa[x][i] != fa[y][i]){x = fa[x][i];y = fa[y][i];}}cout << d[fa[x][0]] << endl;}return 0;
}

 接下来是01trie

01trie是一种二叉树,每一个叶子节点对应一个二进制数,通过从根出发到该节点的路径表示,深度小的表示二进制高位

01trie通常用于解决最大异或对问题,即求解a[i]^a[j]的最大值的问题

01trie支持三种操作

1.插入元素x

2.查询01trie中与x异或后最大的元素

3.查询比x更大的元素个数,可用于解决逆序对问题

还可以结合二分等算法实现更多操作

这样就可以创建一个01trie,与字典树几乎一样

01trie的插入

void insert(int x)
{int now=1;e[now]++;for(int i=30;i>=0;i--){  int y=(x>>i)&1;if(!nxt[now][y])nxt[now][y]=++cnt;now=nxt[now][y];e[now]++;}
}

01trie的查询

//查询01trie中与x异或的最大值
int query(int x)
{int now=1;int res=0;for(int i=30;i>=0;i--){int y=(x>>i)&1;if(nxt[now][!y])now=nxt[now][!y],res+=(1<<i);else now=nxt[now][y];}return res;
}
//查询01trie中比x大的数目
int query(int x)
{int res=0;int now=1;for(int i=30;i>=0;i--){int y=(x>>i)&1;if(y==0)res+=e[nxt[now][1]);if(!nxt[now][y])break;now=nxt[now][y];}return res;
}

 下面来写一道例题蓝桥17205卓儿的最大异或和

考虑异或的性质,将前缀异或添加到字典树中,每次询问求与当前前缀异或的最大值,就可以询问到每一个区间,每次取max就能得到答案

ac代码如下

#include<iostream>
using namespace std;
using ll=long long;
const int N=32*1e5+10;
int nxt[N][2];
int n,cnt=1;
ll pre[N];
void insert(int x)
{int now=1;for(int i=30;i>=0;i--){int y=(x>>i)&1;if(!nxt[now][y])nxt[now][y]=++cnt;now=nxt[now][y];}
}
ll query(ll x)
{int now=1;ll res=0;for(int i=30;i>=0;i--){int y=(x>>i)&1;if(nxt[now][!y])now=nxt[now][!y],res+=(1ll<<i);else now=nxt[now][y];}return res;
}
int main()
{ios::sync_with_stdio(0),cin.tie(0);cin>>n;int x;ll ans=-1;insert(0);for(int i=1;i<=n;i++){cin>>x;pre[i]=pre[i-1]^x;ans=max(ans,query(pre[i]));insert(pre[i]);}cout<<ans<<endl;
}

 接下来是关于01trie删点求最大异或

蓝桥19721最大异或和

考虑枚举每一个节点,然后删除与它相邻的节点,求一次最大异或和,再将点添加回去

ac代码如下

#include<iostream>
#include<vector>
using namespace std;
const int N=32*1e5+5;
int nxt[N][2];
int e[N];
int n,cnt=1;
void insert(int x)
{int now=1;e[now]++;for(int i=30;i>=0;i--){int y=(x>>i)&1;if(!nxt[now][y])nxt[now][y]=++cnt;now=nxt[now][y];e[now]++;}
}
void remove(int x)
{int now=1;e[now]--;for(int i=30;i>=0;i--){int y=(x>>i)&1;now=nxt[now][y];e[now]--;}
}
int query(int x)
{int res=0;int now=1;for(int i=30;i>=0;i--){int y=(x>>i)&1;if(nxt[now][!y]&&e[nxt[now][!y]])now=nxt[now][!y],res|=(1<<i);else now=nxt[now][y];}return res;
}
int main()
{ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);cin>>n;vector<int>a(n+1);vector<vector<int>> p(n+1,vector<int>());for(int i=1;i<=n;i++){cin>>a[i];insert(a[i]);}for(int i=1;i<=n;i++){int x;cin>>x;if(x!=-1){p[x+1].push_back(i);p[i].push_back(x+1);}}int ans=0;for(int i=1;i<=n;i++){for(auto y:p[i])remove(a[y]);ans=max(ans,query(a[i]));for(auto y:p[i])insert(a[y]);}cout<<ans<<endl;
}

最后来用01trie求逆序对

 

建树,然后就求出来了

#include<iostream>
#include<vector>
using namespace std;
using ll=long long;
const int N=32*1e5+10;
int nxt[N][2],e[N],n,cnt=1;
void insert(int x)
{int now=1;e[now]++;for(int i=30;i>=0;i--){int y=(x>>i)&1;if(!nxt[now][y])nxt[now][y]=++cnt;now=nxt[now][y];e[now]++;}
}
int query(int x)
{int now=1;int res=0;for(int i=30;i>=0;i--){int y=(x>>i)&1;if(y==0)res+=e[nxt[now][1]];if(!nxt[now][y])break;now=nxt[now][y];}return res;
}
int main()
{ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);cin>>n;ll ans=0;for(int i=1;i<=n;i++){int x;cin>>x;insert(x);ans+=query(x);}cout<<ans<<endl;
}

 欧克了

 

 

相关文章:

字典树与01trie

字典树简介 当我们通过字典查一个字或单词的时候&#xff0c;我们会通过前缀或关键字的来快速定位一个字的位置&#xff0c;进行快速查找。 字典树就是类似字典中索引表的一种数据结构&#xff0c;能够帮助我们快速定位一个字符串的位置。 字典树是一种存储字符串的数据结构…...

vue - [Vue warn]: Duplicate keys detected: ‘0‘. This may cause an update error.

问题描述&#xff1a; vue项目中&#xff0c;对表单数组赋值时&#xff0c;控制台抛出警告&#xff1a; 问题代码&#xff1a; 问题分析&#xff1a; 1、Vue 要求每个虚拟 DOM 节点必须有唯一的 key。该警告信息通常出现在使用v-for循环的场景中&#xff0c;多个同级节点使用…...

mysql中show命令的使用

在 MySQL 中&#xff0c;SHOW 命令是一个非常实用的工具&#xff0c;用于查询数据库元数据&#xff08;如数据库、表、列、索引等信息&#xff09;。以下是常见的 SHOW 命令及其用法&#xff1a; 1. 显示所有数据库 SHOW DATABASES;列出服务器上的所有数据库。 2. 显示当前数据…...

各类神经网络学习:(三)RNN 循环神经网络(中集),同步多对多结构的详细解释

上一篇下一篇RNN&#xff08;上集&#xff09;RNN&#xff08;下集&#xff09; 同步多对多结构 1&#xff09;结构详解 ①图解&#xff1a; ②参数含义&#xff1a; x t x_t xt​ &#xff1a;表示每一个时刻的输入&#xff1b; o t o_t ot​ &#xff1a;表示每一个时刻的输…...

Python Web 框架 Django、Flask 和 FastAPI 对比

在探索 Python Web 框架时&#xff0c;Django、Flask 和 FastAPI 无疑是最常被提及的名字。根据我们最新的 Python 开发者调查&#xff0c;这三大框架继续稳坐后端 Web 开发的热门宝座。它们均为开源项目&#xff0c;并且与 Python 的最新版本无缝兼容。然而&#xff0c;面对不…...

Hyperlane 似乎是一个轻量级、高性能的 Rust HTTP 服务器库

关键要点 Hyperlane 是一个轻量级、高性能的 Rust HTTP 服务器库&#xff0c;适合简化网络服务开发。它支持 HTTP 请求解析、响应构建、TCP 通信&#xff0c;并提供中间件、WebSocket 和服务器发送事件&#xff08;SSE&#xff09;功能。安装通过 cargo add hyperlane 完成&am…...

【计算机网络运输层详解】

文章目录 一、前言二、运输层的功能1. 端到端通信2. 复用与分用3. 差错检测4. 流量控制5. 拥塞控制 三、运输层协议&#xff1a;TCP 和 UDP1. TCP&#xff1a;面向连接的可靠传输协议2. UDP&#xff1a;无连接的传输协议 四、端口号与进程通信1. 端口号分类2. 端口通信模型 五、…...

UR5e机器人位姿

UR5e 作为一款 6 自由度协作机器人&#xff0c;其末端执行器的位姿&#xff08;位置与姿态的组合&#xff09;控制是实现精准操作的核心。在笛卡尔坐标系中&#xff0c;位姿通常用齐次变换矩阵表示&#xff0c;包含末端的三维位置&#xff08;x, y, z&#xff09;和三维姿态&am…...

导入 Excel 规则批量修改或删除 PDF 文档内容

需要对 PDF 文档内容进行修改的时候&#xff0c;通常我们会需要借助一些专业的工具来帮我们完成。那我们如果需要修改的 PDF 文档较多的时候&#xff0c;有什么方法可以帮我们实现批量操作呢&#xff1f;今天这篇文章就给大家介绍一下当我们需要批量修改多个 PDF 文档的时候&am…...

大模型tokenizer重构流程

大模型tokenizer层再训练&#xff08;选取Qwen7B试验&#xff0c;重构token层&#xff09; 最近公司可能想训练一个蛋白质大模型&#xff0c;需要了解一下大模型tokenizer重构&#xff0c;之后可能要训练&#xff0c;这里做了一定的总结。 文章目录 1. 首先查看Qwen2.5 7B基本…...

JAVA线程安全的集合类分类

1. 传统同步集合类&#xff08;早期实现&#xff0c;性能较低&#xff09;‌ ‌Vector‌ 动态数组实现&#xff0c;所有方法通过 synchronized 同步锁保证线程安全‌。 ‌Stack‌ 继承自 Vector&#xff0c;实现后进先出&#xff08;LIFO&#xff09;堆栈&#xff0c;同步锁机…...

ISIS-1 ISIS概述

前面几章我们介绍了OSPF的基础工作原理以及怎样交互LSA形成LSDB链路状态数据库的 这一章我们来介绍另一个链路状态路由协议,ISIS路由协议 一、概述 ISIS(Intermediate System to Intermediate System,中间系统到中间系统)是由ISO(International Organization for Standardiza…...

茱元游戏TV2.9.3 | 适配多设备的经典街机游戏集合

茱元游戏TV是一款专为TV端设计的游戏软件&#xff0c;同时适配手机、投影仪和车机等多种设备。尽管其兼容性一般&#xff0c;仅支持安卓9.0以上系统&#xff0c;但它提供了丰富的经典街机游戏资源&#xff0c;非常适合8090后怀旧游玩。注意&#xff0c;游戏需先下载才能玩&…...

RTD2525BE《HDMI转EDP,DP转EDP》显示器芯片

一、产品概述 瑞昱RTD2525BE是一款专为高端显示设备设计的多接口转换芯片&#xff0c;支持HDMI 2.0与DisplayPort&#xff08;DP&#xff09;1.4双输入&#xff0c;并高效转换为嵌入式DisplayPort&#xff08;eDP&#xff09;输出。该芯片集成先进信号处理技术&#xff0c;支持…...

SvelteKit 最新中文文档教程(10)—— 部署 Cloudflare Pages 和 Cloudflare Workers

前言 Svelte&#xff0c;一个语法简洁、入门容易&#xff0c;面向未来的前端框架。 从 Svelte 诞生之初&#xff0c;就备受开发者的喜爱&#xff0c;根据统计&#xff0c;从 2019 年到 2024 年&#xff0c;连续 6 年一直是开发者最感兴趣的前端框架 No.1&#xff1a; Svelte …...

springboot使用阿里限流框架-sentinel

当前项目源码 控制台下载 启动bin中的看板服务&#xff1a;账号密码:sentinel/sentinel 官方文档地址 项目引入依赖 <!-- sentinel注解支持 --> <dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-annotation-aspectj<…...

鸿蒙特效教程10-卡片展开/收起效果

鸿蒙特效教程10-卡片展开/收起效果 在移动应用开发中&#xff0c;卡片是一种常见且实用的UI元素&#xff0c;能够将信息以紧凑且易于理解的方式呈现给用户。 本教程将详细讲解如何在HarmonyOS中实现卡片的展开/收起效果&#xff0c;通过这个实例&#xff0c;你将掌握ArkUI中状…...

Qt在模块依靠情况下资源文件名称和资源名称的使用限制

概述 在Qt中使用添加资源文件的时候&#xff0c;对于资源文件名称的定义&#xff0c;往往是较为随意的。 但是当涉及到Qt库依赖的时候&#xff0c;则可能需要遵守一定的规则&#xff0c;否则可能出现文件找不到或者错误加载的问题。 环境 环境名称Qt 版本系统版本LinuxQt 5.…...

MTK Android12-Android13 设置系统默认语言

Android 系统&#xff0c;默认语言 文章目录 需求&#xff1a;场景 参考资料实现方案实现思路编译脚本熟悉-平台熟悉mssi_64_cnkernel-4.19 解决方案修改文件-实现方案 源码分析PRODUCT_LOCALES 引用PRODUCT_DEFAULT_LOCALE 定义get-default-product-locale 方法定义PRODUCT_DE…...

【003安卓开发方案调研】之ReactNative技术开发安卓

基于2025年最新行业动态和搜索资料&#xff0c;以下是针对国内使用React Native&#xff08;RN&#xff09;开发安卓应用的深度分析&#xff1a; 一、技术成熟度评估 1. 核心架构升级 新架构全面普及&#xff1a;2024年起&#xff0c;React Native的 新架构&#xff08;Fabri…...

CSS3学习教程,从入门到精通,CSS3 浮动与清除浮动语法知识点及案例代码(14)

CSS3 浮动与清除浮动语法知识点及案例代码 一、浮动基础 浮动语法 选择器 {float: left|right|none|initial|inherit; }left&#xff1a;元素向左浮动。right&#xff1a;元素向右浮动。none&#xff1a;默认值&#xff0c;元素不浮动。initial&#xff1a;使用默认值。inhe…...

贪心算法——思路与例题

贪心算法&#xff1a;当我们分析一个问题时&#xff0c;我们往往先以最优的方式来解决问题&#xff0c;所以顾名思义为贪心。 例题1 题目分析&#xff1a;这题利用贪心算法来分析&#xff0c;最优解&#xff08;可容纳人数最多时&#xff09;一定是先考虑六人桌&#xff0c;然…...

网络华为HCIA+HCIP 防火墙

防火墙部署模式 路由模式 有路由器的功能 路由器干的活 他都得干 透明模式 旁挂模式 IDS 端口镜像 VPN...

WordPress超级菜单插件UberMenu v3.78汉化版

一、插件介绍 UberMenu 是一款功能强大的 WordPress 超级菜单插件,能够帮助站长创建响应式、可自定义的多级菜单。该插件支持动态内容加载、图标、图片、搜索框等丰富功能,并且兼容大多数 WordPress 主题。 UberMenu v3.78 经过完整汉化,适用于中文站点用户,让操作更加直观…...

SQL中体会多对多

我们可以根据学生与课程多对多关系的数据库模型&#xff0c;给出实际的表数据以及对应的查询结果示例&#xff0c;会用到JOINLEFT JOIN两种连接 1. 学生表&#xff08;students&#xff09; student_idstudent_name1张三2李四3王五 2. 课程表&#xff08;courses&#xff09…...

23种设计模式-备忘录(Memento)设计模式

备忘录设计模式 &#x1f6a9;什么是备忘录设计模式&#xff1f;&#x1f6a9;备忘录设计模式的特点&#x1f6a9;备忘录设计模式的结构&#x1f6a9;备忘录设计模式的优缺点&#x1f6a9;备忘录设计模式的Java实现&#x1f6a9;代码总结&#x1f6a9;总结 &#x1f6a9;什么是…...

2024年3月全国计算机等级考试真题(二级C语言)

&#x1f600; 第1题 下列叙述中正确的是 A. 矩阵是非线性结构 B. 数组是长度固定的线性表 C. 对线性表只能作插入与删除运算 D. 线性表中各元素的数据类型可以不同 题目解析&#xff1a; A. 矩阵是非线性结构 错误。矩阵通常是二维数组&#xff0c;属…...

计算机网络基础之三种交换技术及其性能分析

一. 交换技术基础 1. 三种交换技术 电路交换&#xff1a;用于电话网络报文交换&#xff1a;用于电报网络分组交换&#xff1a;用于现代计算机网络 2. 人类历史上的通信网络 #mermaid-svg-AeGvrkUbCkicFOIo {font-family:"trebuchet ms",verdana,arial,sans-serif;…...

使用python爬取网络资源

整体思路 网络资源爬取通常分为以下几个步骤&#xff1a; 发送 HTTP 请求&#xff1a;使用requests库向目标网站发送请求&#xff0c;获取网页的 HTML 内容。解析 HTML 内容&#xff1a;使用BeautifulSoup库解析 HTML 内容&#xff0c;从中提取所需的数据。处理数据&#xff…...

【MySQL】索引 事务

目录 一、索引 概念 作用 使用场景 使用 查看索引 创建索引 删除索引 背后的数据结构 二、事务 为什么使用事务 事务的概念 使用 开启事务 执行多条 SQL 语句 回滚或提交&#xff1a;rollback/commit; 事务的基本特性 原子性 一致性 持久性 隔离性 脏读 …...