本系列所有题目均为Acwing课的内容,发表博客既是为了学习总结,加深自己的印象,同时也是为了以后回过头来看时,不会感叹虚度光阴罢了,因此如果出现错误,欢迎大家能够指出错误,我会认真改正的。同时也希望文章能够让你有所收获,与君共勉!

字典树

今天来学习字典树Trie,Trie树主要支持两个操作,一个是构建Trie时需要的插入操作Insert,另一个是查询操作query

首先来看看什么是字典树Trie,标准的Trie树是字符串组成的,结点是每个字符。图片来自这里

如果是数组来模拟Trie树,就是这样。

既然是树,那么必然存在一个根节点,只不过这里是虚拟节点root,由于小写字符总共有26个,因此总共有26个子节点。数组是这样定义的:son[p][u],其中p可以认为Trie树的深度(从根节点开始算,root为-1层),也可以理解为p服从某种顺序。u是该层所存储的东西,可以是数字也可以是字母(需要映射为数字),而son[p][u]就可以理解为第p层上的字母u的下一层索引(,对于u来说其是固定的,如果存在该字符我们就可以直接使用并通过son[p][u]获得该字符下一层所对应的结点索引),除此之外还有一个idx,它的作用类似一个线性表的在当前位置上的索引,具体作用看代码,这样对于每一个字符就得到了一个具体而准确的定位。

Trie的insert操作:

主要分两种情况,从0开始遍历层数,如果下一层存在该字符,就继续往下走,如果不存在就在下一层该字符处创建新的索引。

Trie的query操作:直接从根节点遍历Trie树,如果某一个字符存在就会自己往下走,否则不存在就可以直接返回。

这就是Trie树的主体,如果对于每个单词,我们想知道他们出现的次数,我们还可以定义cnt数组,来标记第p层存在单词,并且用来表示存在几个单词,而对于是否存在单词我们是在遍历过程中才能发现的,如果不存在某一个单词,那么遍历都不会完成,深度也不会达到p的。

代码表示

#include <iostream>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std; const int N = 100000,idx;
int son[N][26],cnt[N];
int n; void insert(char* s){
int p = 0;
for(int i=0; s[i] ; ++i){
int u = s[i] - 'a';
if(!son[p][u]) son[p][u] = ++idx;
// idx可以看成是深度所形成的线性表的下标,++idx在第一次循环就表示根节点在第0层,第一个结点在第一层
p = son[p][u]; // 就是去下一层
}
cnt[p] ++;
} int query(char *s){
int p = 0;
for(int i=0 ; s[i] ; ++i){
int u = s[i] - 'a';
if(!son[p][u]) return 0;
p = son[p][u];
}
return cnt[p];
} int main()
{
cin >> n;
while (n -- ){
char op[2];
char s[N];
scanf("%s%s",op,s);
if(*op == 'I'){
Insert(s); }
else{
cout << Query(s) << endl;;
}
}
return 0;
}

最大异或对

在给定的 N 个整数 A1,A2……AN 中选出两个进行 xor(异或)运算,得到的结果最大是多少?

输入格式

第一行输入一个整数 N。

第二行输入 N 个整数 A1~AN。

输出格式

输出一个整数表示答案。

数据范围

1≤N≤105,

0≤Ai<231

输入样例:

3

1 2 3

输出样例:

3

算法原理

遇到题我们先思考暴力怎么求解,然后再针对每一步考虑使用合适的数据结构,对于最大异或对我们先考虑暴力双重循环,两两之间比较异或值,最后选出最大的,核心代码max(res,a[i]^a[j])(其中^是异或运算符),但这个算法的时间复杂度太高,\(O(n^2)\),因此我们需要思考怎么进行优化,我们发现最外层遍历每一个数字不能优化,内层我们需要找出外层固定的这个数字的最大异或值,那么我们就是要针对找到最大异或值这一部分进行优化。我们知道两个数字的异或运算就是对位相反取1,相同取0,如2:10,3:11,2^3:01。那么什么时候取最大值呢,我们发现当每一位都位1时所对应的数字是最大的,高位为1时的数字比低位为1时要更大,所以对位相反时进行异或结果是最大的,我们更希望每一位都相反,因此我们可以先寻找是否存在该数字的最大异或值,这时我们就可以使用Trie树进行寻找,先来看个图就理解为什么要用Trie树啦。图片来自这里



这样做的话找到的不就是该数组存储的所有数中最大的异或值了嘛。

代码实现

#include <iostream>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std; const int N = 3300000;
int son[N][2],a[N]; // 只有0和1两个数,因此是二叉树且默认所有结点的下一层都为第0层(即不存在下一层)
int idx; void insert(int x){ // 插入x的每一位,形成异或树
int p = 0;
for(int i=30; i>=0 ; --i){
int u = x>>i & 1; // 获得x的第i位
if(!son[p][u]){
son[p][u] = ++idx;
}
p = son[p][u];
}
} int search(int x){ // 查询x的最大异或数
int p = 0;
int ans = 0; // 所有位默认为0
for(int i=30; i>=0 ; --i){
int u = x >> i & 1; // 取出第i位
// 判断是否存在对位,如果存在就往下走,否则就走另一个
if(son[p][!u]) {// 存在对位,说明该位可以是最大的,因此该位为1
ans += 1<<i; // 把1向左移到第i位
p = son[p][!u]; // 走的不是原来的位置,而是对位
}
else{ // 不存在对位,所以该位就是0
p = son[p][u]; // ans为0说明当前该位默认为0,直接移到下一位
}
}
return ans;
} int main()
{
int n;
cin >> n;
int x;
for(int i=0; i < n; ++i){
cin >> a[i];
insert(a[i]);
}
int res= 0;
for(int i=0; i<n ; ++i) // 找到每一个数的最大异或值,再找到所有的数的最大异或值
{
res = max(res,search(a[i]));
}
cout << res << endl;
return 0;
}

2022-11-13 Acwing每日一题的更多相关文章

  1. [ 9.13 ]CF每日一题系列—— 340A GCD & LCM

    Description: [ 着实比较羞愧,都想着去暴力,把算法(方法)也忘了] A只涂x,2x,3x……,B只涂y,2y,3y……问你A和B共同涂的墙的个数 Solution: 就是求x和y的lcm ...

  2. CISP/CISA 每日一题 13

    监控信息系统人员所提供服务的效率和效果的工具: 1.例外报告:识别所有没有成功完成的或出了故障的应用 2.作业重运行报告:大多数异常终止作业都会导致重起 3.操作员问题报告:记录计算机运行问题及解决方 ...

  3. CISP/CISA 每日一题 11

    CISA 每日一题(答) 一个合理建造的数据仓库应当支持下列三种基本的查询格式: 1.向上溯源和向下溯源——向上溯源是对数据进行总计:向下溯源是将数据进行细化: 2.交叉溯源——通过通用属性访问数据仓 ...

  4. 老男孩IT教育-每日一题汇总

    老男孩IT教育-每日一题汇总 第几天 第几周 日期 快速访问链接 第123天 第二十五周 2017年8月25日 出现Swap file….already exists以下错误如何解决? 第122天 2 ...

  5. CISP/CISA 每日一题 五

    CISA 每日一题(答) 信息系统审计师要确认系统变更程序中的: 1.变更需求应有授权.优先排序及跟踪机制: 2.日常工作手册中,明确指出紧急变更程序: 3.变更控制程序应同时为用户及项目开发组认可: ...

  6. [每日一题]面试官问:谈谈你对ES6的proxy的理解?

    [每日一题]面试官问:谈谈你对ES6的proxy的理解? 关注「松宝写代码」,精选好文,每日一题 作者:saucxs | songEagle 一.前言 2020.12.23 日刚立的 flag,每日一 ...

  7. 【python】Leetcode每日一题-寻找旋转排序数组中的最小元素

    [python]Leetcode每日一题-寻找旋转排序数组中的最小元素 [题目描述] 已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组.例如,原数组nums ...

  8. 【JavaScript】【dp】Leetcode每日一题-解码方法

    [JavaScript]Leetcode每日一题-解码方法 [题目描述] 一条包含字母 A-Z 的消息通过以下映射进行了 编码 : 'A' -> 1 'B' -> 2 ... 'Z' -& ...

  9. [LeetCode每日一题]153.寻找旋转排序数组中的最小值

    [LeetCode每日一题]153.寻找旋转排序数组中的最小值 问题 已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组.例如,原数组 nums = [0,1, ...

  10. 【python】Leetcode每日一题-逆波兰表达式求值

    [python]Leetcode每日一题-逆波兰表达式求值 [题目描述] 根据 逆波兰表示法,求表达式的值. 有效的算符包括 +.-.*./ .每个运算对象可以是整数,也可以是另一个逆波兰表达式. 说 ...

随机推荐

  1. 记录一次数据库CPU被打满的排查过程

    1 前言 近期随着数据量的增长,数据库CPU使用率100%报警频繁起来.第一个想到的就是慢Sql,我们对未合理运用索引的表加入索引后,问题依然没有得到解决,深入排查时,发现在 order by id ...

  2. OpenJudge 1.5.24 正常血压

    24:正常血压 总时间限制: 1000ms 内存限制: 65536kB 描述 监护室每小时测量一次病人的血压,若收缩压在90 - 140之间并且舒张压在60 - 90之间(包含端点值)则称之为正常,现 ...

  3. Configuration的学习

    创建 //1.创建,调用的空惨 Configuration conf = new Configuration(); 加载主配置 //2.读取主配置文件==>如果是空参方法则自动加载sec下的re ...

  4. ping: sina.cn: Name or service not known

    该方法针对Ubuntu18及以后版本. 第一次遇到ping:报错Name or service not known这个问题在百度上找了很久说的都是什么修改 /etc/resolv.conf,但每次修改 ...

  5. 从源码中理解Spring Boot自动装配原理

    个人博客:槿苏的知识铺 一.什么是自动装配 SpringBoot 定义了一套接口规范,这套规范规定:SpringBoot在启动时会扫描外部引用jar包中的META-INF/spring.factori ...

  6. 类的常成员const

    成员数据 以Human为例: 类中: 传统的初始化方式:                                                                        ...

  7. Java SE 7、接口

    接口 接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,在根据具体情况把这些方法写出来 语法 interface 接口名{ ​ //属性 ​ //方法 } class 类名 imple ...

  8. Bugly iOS自动导入符号表

      前言       最近在处理Bugly问题的时候顺便解决了下符号表上传的问题,使用最新的上传工具包,也是顺便整理了下可以使用的脚本添加到了项目中,把这个过程中遇到的问题总结出来,脚本也会给出来,实 ...

  9. 不给字段创建索引,字段不存放在source中,字段无法聚合查询等

    某个字段不被搜索,也就是说不想为这个字段建立inverted index(反向索引),可以这么做: PUT twitter { "mappings": { "uid&qu ...

  10. LeetCode - 数组遍历

    1. 485. 最大连续 1 的个数 1.1 分析题意 首先:我们求的是连续的1的个数,所以我们不能也没必要对数组进行排序: 其次:只要求求出最大连续1的个数,并不要求具体的区间数目,所以我们只需要用 ...