ch05 课堂参考代码及部分题目思路
ch05 字典树
字典树(Trie)是一种用于实现字符串快速查找的多叉树结构,查找原理类似于我们在英文词典上查找单词。
字典树用边来代表字母,从根结点到树上某一结点的路径就代表了一个字符串。
字典树的表示
- 以字符集为小写字母的字典树为例,是一棵 26 叉树,每个结点都需要记录它的 26 个子结点编号(就像二叉树中用 lc 和 rc 记录左右子结点编号),ch[0] 记录 ‘a’ 这条边对应的子结点编号,ch[1] 记录往 ‘b’ 这条边对应的子结点编号,以此类推。ch[i] = 0 表示还没有这条边(还没有这个子结点)。
- 为了支持词频统计、前缀匹配相关的查找,每个结点还需要记录 cnt 和 siz。cnt 表示从根结点到当前结点的路径所代表的字符串的个数(或者理解为当前结点是 cnt 个字符串的结尾),siz 是子树的 cnt 之和(可以发现这里的 cnt 和 siz 的含义类似“二叉查找树”中的 cnt 和 siz)。
- 初始化:一棵空的字典树仅包含根结点(根结点编号为 0),ch[i] 、cnt、siz 均为 0。
const int maxn = 1000010;
struct Node{int ch[26];int cnt, siz;
} trie[maxn];
int tot; // 已经使用的结点编号 0~tot,下一次新结点编号为 tot+1
插入操作
- 当需要插入一个字符串 s 时,从根结点出发,然后依次扫描 s 中的每个字符 s[i] ,根据 s[i] 这个字符决定往哪个子结点走,子结点不存在时新建结点。同时进行 cnt 和 siz 的修改。
// 往字典树中插入字符串s
void ins(string s) {int u = 0;for (int i = 0; i < s.size(); i++) {int p = s[i] - 'a';if (trie[u].ch[p] == 0) trie[u].ch[p] = ++tot;u = trie[u].ch[p];trie[u].siz++;}trie[u].cnt++;
}
查询操作
-
查询字符串 s 是否在字典树中
- 类似插入操作,从根结点往下走,有两个情况说明不存在字符串 s:一是往下走的过程中子结点不存在,走不通;二是走到字符串 s 的结尾结点了,但是该结点的 cnt == 0,说明之前插入的字符串的前缀有 s,但没有插入过字符串 s 。
bool query1(string s) {int u = 0;for (int i = 0; i < s.size(); i++) {int p = s[i] - 'a';if (trie[u].ch[p] == 0) return false;u = trie[u].ch[p];}return trie[u].cnt > 0; } -
查询有多少个字符串的前缀有 s
int query2(string s) {int u = 0;for (int i = 0; i < s.size(); i++) {int p = s[i] - 'a';if (trie[u].ch[p] == 0) return 0;u = trie[u].ch[p];}return trie[u].siz; } -
查询有多少个字符串是s的前缀
int query3(string s) {int u = 0, sum = 0;for (int i = 0; i < s.size(); i++) {int p = s[i] - 'a';if (trie[u].ch[p] == 0) return sum;u = trie[u].ch[p];sum += trie[u].cnt;}return sum; }
遍历
- 遍历字典树,类似二叉树的遍历,只是改为 26 叉树的遍历
void dfs(int u) {// 当前访问到字典树上编号为u的结点for (int i = 0; i < 26; i++) {int v = trie[u].ch[i]; // u的第i个子结点if (v) dfs(v); // 子结点v存在,往v搜索}
}
复杂度
- 时间复杂度:插入和查询都是遍历字符串,从根结点往下走,所以时间复杂度是 O ( ∣ s ∣ ) O(|s|) O(∣s∣) , ∣ s ∣ |s| ∣s∣ 表示字符串 s 的长度。
- 空间复杂度: O ( N C ) O(NC) O(NC) ,其中 N 是结点数量(所有字符串长度之和),C 是字符集大小(仅包含小写字母则 C = 26)。
- 字典树时间复杂度很好,空间复杂度比较差,是一种空间换时间的数据结构。
01 字典树
01 字典树是一种特殊的字典树,字符集只有 0 和 1(二叉树),可以维护整数二进制异或的相关信息。
例题:最大异或查询
-
分析:
- 题目要查找 x 与另一个数异或之后的最大结果,按照贪心的思路,优先让结果的二进制高位尽量是 1,这样结果最大。
- 对于要添加的整数 x,将 x 的二进制插入字典树中,从高位往低位插入,这里要考虑 x 的二进制最多有多少位,例如 1 0 18 10^{18} 1018 二进制最多 60 位,那么从第 60 位开始插入(从第 0 位开始数则是第 59 位,这里位数算多一点没有关系,算少了就会错误)。
- 查询时,从高位往低位,要让当前位的异或结果尽量是 1,那么就要往相反方向搜索,x 这一位是 1,则要往 0 的子结点走,x 这一位是 0,则要往 1 的子结点走,子结点不存在就只能往相同方向搜索。
-
主要代码:
// 插入数字x的二进制表示(从高位到低位插入) void ins01(ll x) {int u = 0;for (int i = 59; i >= 0; i--) {int p = (x >> i) & 1;if (trie[u].ch[p] == 0)trie[u].ch[p] = ++tot;u = trie[u].ch[p];trie[u].siz++;}trie[u].cnt++; } // 查询与x异或的最大结果 ll queryMaxXor(ll x) {int u = 0;ll res = 0;for (int i = 59; i >= 0; i--) {int p = (x >> i) & 1;if (trie[u].ch[p ^ 1]) {res |= (1LL << i);u = trie[u].ch[p ^ 1];} elseu = trie[u].ch[p];}return res; }
ch05 部分题目解法
偏序统计
-
分析:
-
注意本题不能像“最大异或查询”那道题一样插入数字 x,在异或问题中,需要权重相同的二进制位对齐,所以二进制都前面补 0 使它们长度相同,这样字典树同一层都是权重相同的。但本题要比较字典序,需要最高位的 1 对齐,不能在前面补 0,例如要比较 11 和 101 的字典序,不能弄成了比较 011 和 101 的字典序。
-
插入数字 x 时,要先处理出 x 的二进制(不含前导 0,最高位是 1)。这里给出参考写法:
// 将x转换为二进制,用字符串储存比较方便 string binaryString(ll x) {string s;do{s += x % 2 + '0';x /= 2;} while (x);reverse(s.begin(), s.end()); // 将逆序的二进制反转为正序return s; } -
对于操作 1,将数字 x 的二进制串 s 从高位往低位插入字典树。
-
对于操作 2,查找字典树中有多少个二进制串字典序 < x 的二进制串 s 。提示:
- 如果 s 的某一位是 1,那么与s前面相同,这一位是 0 的字符串字典序都 < s
- 此外,s 的真前缀(除了 s 本身的 s 的前缀)字典序也比 s 小,例如 11 的字典序 < 110
int query(string s) {int u = 0, res = 0;for (int i = 0; i < s.size(); ++i) {res += trie[u].cnt; // s 前缀比 s 小int p = s[i] - '0';// 如果朝右走,加上左子树中单词数量if (p == 1) res += trie[trie[u].ch[0]].siz;if (!trie[u].ch[p]) return res;u = trie[u].ch[p];}return res; }
-
Secret Message
- 分析:
- 本题题意是对于每条暗号 s,查询前面的 n 条信息中,有多少条信息满足 “前缀有 s” 或 “是 s 的前缀”,即课堂代码中的 query2(s) + query3(s) 。
- 但直接 query2(s) + query3(s) 会有问题,走到结尾结点 u 时,u 的 siz 是包含了 u 的 cnt 的,这样算就会重复统计 u 的 cnt,思考怎么避免重复计算
相关文章:
ch05 课堂参考代码及部分题目思路
ch05 字典树 字典树(Trie)是一种用于实现字符串快速查找的多叉树结构,查找原理类似于我们在英文词典上查找单词。 字典树用边来代表字母,从根结点到树上某一结点的路径就代表了一个字符串。 字典树的表示 以字符集为小写字母的…...
0328-内存图2
是否正确待定: Perso类 package com.qc.内存图2;public class Perso {public int age;public String name;public static int flag;public void m1() {}public static void m2() {}Overridepublic String toString() {return "Perso [age" age "…...
【ESP32S3】esp32获取串口数据并通过http上传到前端
通过前面的学习(前面没发过,因为其实就是跑它的demo)了解到串口配置以及开启线程实现功能的工作流程,与此同时还有esp32作为STA节点,将数据通过http发送到服务器。 将这两者联合 其实是可以得到一个:esp32获…...
代购系统:架构设计、功能实现与用户界面优化
一、引言 随着全球化的加速,代购业务已成为电商领域的重要组成部分。代购系统不仅需要满足用户对商品的需求,还需提供高效、安全、便捷的购物体验。本文将从技术架构设计、功能实现、用户界面优化三个方面深入探讨代购系统的设计与实现。 二、技术架构…...
《一本书讲透Elasticsearch:原理、进阶与工程实践》读书笔记
1:es的组成部分: Elasticsearch 引擎:核心组件,处理索引和搜索请求 Kibana:es的可视化的数据界面,用于分析和展示数据 Beats(可选)轻量级的日志采集器 2:基本概念 es开…...
Android15查看函数调用关系
Android15 Camera3中打印函数调用栈 1.使用CallStack跟踪函数调用 修改涉及三个内容: Android.bp中添加对CallStack的引用。CallStack被打包在libutilscallstack.so。代码中包含CallStack的头文件。代码中调用CallStack接口,打印函数调用栈。 例子&am…...
Spring Boot(十七):集成和使用Redis
Redis(Remote Dictionary Server,远程字典服务器)是一个开源的、基于内存的数据结构存储系统,它可以用作数据库、缓存和消息中间件。Spring Boot 中集成和使用Redis主要涉及以下几个步骤: 添加依赖 在项目的pom.xml文件中添加Redis的依赖。Spring Boot提供了对Redis的集…...
macOS 15 通过 MacPorts 安装 PHP 7 构建错误找不到符号在 dns.o 中解决方法
构建遇到的问题如下: "_res_9_dn_expand", referenced from:_php_parserr in dns.o_php_parserr in dns.o_php_parserr in dns.o_php_parserr in dns.o_php_parserr in dns.o_php_parserr in dns.o_zif_dns_get_mx in dns.o..."_res_9_dn_skipname&…...
练习:猜数字小游戏
需求: 程序自动生成一个 1 - 100 之间的随机数字,使用程序实现猜出这个数字是多少? 代码: //猜数字小游戏 package demo01; import java.util.Random; import java.util.Scanner; public class HelloJava {public static void …...
EMQX Dashboard
EMQX Dashboard EMQX理论基础 https://blog.csdn.net/liudachu/article/details/146495030 1 Dashboard简介 EMQX 提供了一个内置的管理控制台,即 EMQX Dashboard。方便用户通过 Web 页面就能轻松管理和监控 EMQX 集群,并配置和使用所需的各项功能。 访…...
PC名词解释-笔记本的S0,S1,S2,S3,S4,S5状态
🎓作者简介:程序员转项目管理领域优质创作者 💌个人邮箱:[2707492172qq.com] 🌐PMP资料导航:PM菜鸟(查阅PMP大纲考点) 💡座右铭:上善若水,水善利…...
uniapp自定义目录tree(支持多选、单选、父子联动、全选、取消、目录树过滤、异步懒加载节点、v-model)vue版本
先看案例: 效果: 数据结构如下: const themeList ref([{id: 1,name: 内蒙古,children: [{id: 3,name: 街道1,children: [{id: 4,name: 小区1}]}]},{id: 2,name: 北京,children: [{id: 6,name: 街道2}]} ]) 参数配置: 属性名类…...
【10】Strongswan collections —— array
//array 代码解释与测试 #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <string.h> #include <stdbool.h> #include <stdarg.h>#define INIT(this, ...) ({ (this) malloc(sizeof(*(this))); \*(this) (typeof…...
ESP32S3 WIFI 实现TCP服务器和静态IP
一、 TCP服务器代码 代码由station_example_main的官方例程修改 /* WiFi station ExampleThis example code is in the Public Domain (or CC0 licensed, at your option.)Unless required by applicable law or agreed to in writing, thissoftware is distributed on an &q…...
docker中安装 python
ubuntu 1、安装源码编译所需依赖 apt-get install -y gcc g make cmake libsqlite3-dev zlib1g-dev libssl-dev libffi-dev 2、下载python安装包 python-release安装包下载_开源镜像站-阿里云 3、解压安装 tar -zxvf Python-3.7.5.tgz cd Python-3.7.5 ./configure --prefix…...
VSCode Flutter 快捷键
扩展安装: Flutter Widget Snippets Flutter Flutter Files 1.StatelessWidget切换StatefulWidget快捷键 1.1 将光标放在 StatelessWidget 上。 1.2 按下快捷键: Windows/Linux: Ctrl . macOS: Cmd . 1.3 在弹出的菜单中选择 "Convert to Stat…...
Java面试黄金宝典18
1. 如何找到一条单链表的中间结点 定义 单链表是一种常见的数据结构,每个节点包含数据和指向下一个节点的指针。找到单链表的中间结点,即找出链表中位于中间位置的节点。可借助快慢指针法达成,快指针每次移动两步,慢指针每次移动…...
设计秒杀系统(高并发的分布式系统)
学海无涯,志当存远。燃心砺志,奋进不辍。 愿诸君得此鸡汤,如沐春风,事业有成。 若觉此言甚善,烦请赐赞一枚,共励学途,同铸辉煌! 思路 处理高并发 流量削峰:限流…...
【面试题】利用Promise实现Websocket阻塞式await wsRequest() 请求
逻辑实现过程 1. 目标与基础设计 目标:实现一个类似 HTTP 请求的阻塞式调用接口(如 await wsRequest(...)),让开发者无需手动处理 WebSocket 的事件回调,而是通过 Promise 和 async/await 获得同步体验。 基础设计&a…...
数据库----单表、多表
数据库 create database 数据库名称;---创建数据库create database 数据库名称 default charsetutf8mb4;---创建数据库,同时指定编码show databases;---查看当前数据库管理下存在多少数据库show databases like "db_%";---查询以db_开头的数据库select d…...
ubuntu 22.04 一键安装 lxd
LXD系列 LXD是一个现代、安全且功能强大的系统容器和虚拟机管理器。 它为在容器或虚拟机中运行和管理完整的 Linux 系统提供了统一的体验。LXD 支持大量 Linux 发行版的映像(官方 Ubuntu 映像和社区提供的映像),并且围绕...
HO与OH差异之Navigation三
在上一篇内容中我们介绍了HO与OH差异之Navigator,我们也了解了Navigator的基本概念和大致了解了一下他的基础用法,既然谈到差异肯定就不止这两种差异,今天就让我们来了解第三种差异NavRouter,其中在HO中我们并没有这种路由方式但是…...
Zookeeper运维指南:服务端与客户端常用命令详解
#作者:任少近 文章目录 1 Zookeeper服务端常用命令2 Zookeeper客户端常用命令2.1Ls命令2.2创建节点create2.3Get命令2.4删除命令2.5修改命令 1 Zookeeper服务端常用命令 启动ZK服务: bin/zkServer.sh start # ./zkServer.sh startZooKeeper JMX enabled by defau…...
linux scp复制多层级文件夹到另一服务器免密及脚本配置
文章目录 生成 SSH 密钥对将公钥复制到目标服务器验证免密登录scp 多级文件夹复制脚本 生成 SSH 密钥对 在本地机器上,使用 ssh-keygen 命令生成 SSH 密钥对。打开终端并执行以下命令: ssh-keygen -t rsa 按提示连续按回车键,默认会在 ~/.ss…...
模型压缩与迁移:基于蒸馏技术的实战教程
1.前言 模型蒸馏(Model Distillation),又称为知识蒸馏(Knowledge Distillation),是一种将大型、复杂的模型(通常称为教师模型,Teacher Model)的知识转移到小型、简单模型…...
XSS通关技巧
目录 第一关: 第二关: 第三关: 第四关: 第五关: 第六关: 第七关: 第八关: 第九关: 第十关: 第十一关: 第十二关: 第十三关:…...
el-tree树多选,将选中的树对象中某个字段值改为true,并过滤出所有为true的对象,组成新的数组
功能实现: el-tree树多选,将选中的树对象中某个字段值改为true,并过滤出所有为true的对象,组成新的数组提交给后端 <template><div><!-- 树形菜单 --><el-tree:data"stageList"show-checkboxdefault-expand-…...
大文件版本管理git-lfs
1. 安装 Git Large File Storage (LFS) 是一个 开源的 Git 扩展,用于替换 Git 仓库中的大文件,用指针文件替代实际的大文件,可以在保持仓库轻量级的同时,有效地管理大型文件。 如果install提示失败,多试几次…...
Android RemoteViews:跨进程 UI 更新的奥秘与实践
目录 一、RemoteViews 的舞台:使用场景 (一)通知栏:动态交互的窗口 (二)桌面小部件:桌面上的动态名片 二、RemoteViews 的本质:定义与架构 (一)什么是 RemoteViews? (二)架构设计:层次分明的协作 (三)操作限制:能力边界在哪里? 三、RemoteViews 的引擎…...
es 3期 第27节-运用Script脚本实现复杂需求
#### 1.Elasticsearch是数据库,不是普通的Java应用程序,传统数据库需要的硬件资源同样需要,提升性能最有效的就是升级硬件。 #### 2.Elasticsearch是文档型数据库,不是关系型数据库,不具备严格的ACID事务特性ÿ…...
