字典树Trie

Trie树又称字典树,前缀树。是一种可以高效查询前缀字符串的树,典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。

它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。`做题看到大量字符串或者大量字符就往Trie树或者哈希这边想,因为速度很快.

image.png

AcWing 835. Trie字符串统计

https://www.acwing.com/activity/content/problem/content/883/

维护一个字符串集合,支持两种操作:

  1. I x 向集合中插入一个字符串 x x x
  2. Q x 询问一个字符串在集合中出现了多少次。

共有 N N N 个操作,所有输入的字符串总长度不超过 1 0 5 10^5 105,字符串仅包含小写英文字母。

输入格式

第一行包含整数 N N N,表示操作数。

接下来 N N N 行,每行包含一个操作指令,指令为 I xQ x 中的一种。

输出格式

对于每个询问指令 Q x,都要输出一个整数作为结果,表示 x x x 在集合中出现的次数。

每个结果占一行。

数据范围

1 ≤ N ≤ 2 ∗ 1 0 4 1 \le N \le 2*10^4 1N2104

输入样例:

5
I abc
Q abc
Q ab
I ab
Q ab

输出样例:

1
0
1

思路

Trie树模版题

代码

/**
 * https://www.acwing.com/problem/content/837/
 */
#include <bits/stdc++.h>

#define int long long
using namespace std;

const int N = 1e5 + 10;
int son[N][26], cnt[N], idx;
// 下标0代表根节点和空节点,cnt用于计数,idx代表结点的索引

void insert(string s) {
    int x = 0;
    for (auto c: s) {
        int y = c - 'a';
        if (!son[x][y]) son[x][y] = ++idx;// 没有该子结点就创建一个
        x = son[x][y]; // 走到该子节点
    }
    ++cnt[x];// 标记该子节点存在的单词个数
}

int query(string s) {
    int x = 0;
    for (auto c: s) {
        int y = c - 'a';
        if (!son[x][y]) return 0;// 没有该子结点就返回查询不到
        x = son[x][y];
    }
    return cnt[x];
}


signed main() {
#ifndef ONLINE_JUDGE
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif
    int n;
    cin >> n;
    while (n--) {
        string op, s;
        cin >> op >> s;
        if (op == "I") insert(s);
        else cout << query(s) << endl;
    }


    return 0;
}

【模板】字典树

https://www.luogu.com.cn/problem/P8306

题目描述

给定 n n n 个模式串 s 1 , s 2 , … , s n s_1, s_2, \dots, s_n s1,s2,,sn q q q 次询问,每次询问给定一个文本串 t i t_i ti,请回答 s 1 ∼ s n s_1 \sim s_n s1sn 中有多少个字符串 s j s_j sj 满足 t i t_i ti s j s_j sj前缀

一个字符串 t t t s s s 的前缀当且仅当从 s s s 的末尾删去若干个(可以为 0 个)连续的字符后与 t t t 相同。

输入的字符串大小敏感。例如,字符串 Fusu 和字符串 fusu 不同。

输入格式

本题单测试点内有多组测试数据

输入的第一行是一个整数,表示数据组数 T T T

对于每组数据,格式如下:
第一行是两个整数,分别表示模式串的个数 n n n 和询问的个数 q q q
接下来 n n n 行,每行一个字符串,表示一个模式串。
接下来 q q q 行,每行一个字符串,表示一次询问。

输出格式

按照输入的顺序依次输出各测试数据的答案。
对于每次询问,输出一行一个整数表示答案。

样例 #1

样例输入 #1

3
3 3
fusufusu
fusu
anguei
fusu
anguei
kkksc
5 2
fusu
Fusu
AFakeFusu
afakefusu
fusuisnotfake
Fusu
fusu
1 1
998244353
9

样例输出 #1

2
1
0
1
2
1

提示

数据规模与约定

对于全部的测试点,保证 1 ≤ T , n , q ≤ 1 0 5 1 \leq T, n, q\leq 10^5 1T,n,q105,且输入字符串的总长度不超过 3 × 1 0 6 3 \times 10^6 3×106。输入的字符串只含大小写字母和数字,且不含空串。

说明

std 的 IO 使用的是关闭同步后的 cin/cout,本题不卡常。

思路

因为这里需要统计的是前缀,因此每走一个点,都需要将cnt数组加1

这里不仅有小写,还有大写和数字,可以把小写映射为0-26,大写映射为26-52,数字映射为36-62 ,都是左闭右开区间。

有多组测试数据,需要初始化son为0,idx为0,cnt为0

数据比较大,需要开cin和cout优化

Trie第一维开多大取决于字符串长度,与idx能增长到多少有关,尽可能开大一点5e6没问题,第二维取决于字符串里面字符的个数

代码

/**
 * https://www.luogu.com.cn/problem/P8306
 */
#include <bits/stdc++.h>

#define int long long
using namespace std;

//空间怎么看开多大?看数据范围 输入总字符串总长度不超过3e6
const int N = 3e6 + 10;
int son[N][65], cnt[N], idx;

int get(char c) {
    if (c >= 'a' && c <= 'z') return c - 'a';
    if (c >= 'A' && c <= 'Z') return c - 'A' + 26;
    if (c >= '0' && c <= '9') return c - '0' + 26 + 26;

}

void insert(string s) {
    int x = 0;
    for (char c: s) {
        int y = get(c);
        if (!son[x][y]) son[x][y] = ++idx;
        x = son[x][y];
        cnt[x]++;
    }
}

int query(string s) {
    int x = 0;
    for (auto c: s) {
        int y = get(c);
        if (!son[x][y]) return 0;
        x = son[x][y];
    }
    return cnt[x];
}


void solve() {
    int n, q;
    cin >> n >> q;
    string s;
    while (n--) { cin >> s, insert(s); }
    while (q--) { cin >> s, cout << query(s) << '\n'; }

    //清空字典树,不使用memset,使用for
    for (int i = 0; i <= idx; i++) {
        for (int j = 0; j < 63; j++) {
            son[i][j] = 0;
        }
    }
    for (int i = 0; i <= idx; i++) cnt[i] = 0;
    idx = 0;
}

signed main() {
#ifndef ONLINE_JUDGE
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif
    cin.tie(0), cout.tie(0), ios::sync_with_stdio(false);
    int t;
    cin >> t;
    while (t--) solve();
    return 0;
}

AcWing 143. 最大异或对

在给定的 N N N 个整数 A 1 , A 2 … … A N A_1,A_2……A_N A1A2……AN 中选出两个进行 x o r xor xor(异或)运算,得到的结果最大是多少?

输入格式

第一行输入一个整数 N N N

第二行输入 N N N 个整数 A 1 A_1 A1 A N A_N AN

输出格式

输出一个整数表示答案。

数据范围

1 ≤ N ≤ 1 0 5 1 \le N \le 10^5 1N105,
0 ≤ A i < 2 31 0 \le A_i < 2^{31} 0Ai<231

输入样例:

3
1 2 3

输出样例:

3

思路

先将所有数插入到01Trie树中,然后遍历一遍数组,去找可以使得和他异或起来最大的数,时间复杂度是 n l o g n nlogn nlogn的,因为树每层最多30个.

如何找异或起来最大:

比如当前节点为0,那就看!0的节点是否存在,如果存在,走过去一定是最优的,因为在这一位异或起来的结果就可以变成1了,否则只能往0走。

代码

/**
 * https://www.acwing.com/problem/content/145/
 */
#include <bits/stdc++.h>

#define int long long
using namespace std;

const int N = 1e5 + 10;
int son[N * 32][2], idx;

void insert(int t) {
    int x = 0;
    for (int i = 30; i >= 0; i--) {
        int y = t >> i & 1;
        if (!son[x][y]) son[x][y] = ++idx;
        x = son[x][y];
    }
}

int query(int t) {
    int x = 0, res = 0;
    for (int i = 30; i >= 0; i--) {
        int y = t >> i & 1;
        if (son[x][!y]) {//另一个存在
            res = res * 2 + !y;
            x = son[x][!y];
        } else {
            res = res * 2 + y;
            x = son[x][y];
        }
    }
    return res;
}


signed main() {
#ifndef ONLINE_JUDGE
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif
    int n;
    cin >> n;
    vector<int> a(n);
    for (int i = 0; i < n; ++i)cin >> a[i];
    for (int i = 0; i < n; ++i) insert(a[i]);
    int mx = 0;
    for (int i = 0; i < n; ++i) mx = max(mx, a[i] ^ query(a[i]));
    cout << mx << endl;


    return 0;
}

最长异或路径

题目描述

给定一棵 n n n 个点的带权树,结点下标从 1 1 1 开始到 n n n。寻找树中找两个结点,求最长的异或路径。

异或路径指的是指两个结点之间唯一路径上的所有边权的异或。

输入格式

第一行一个整数 n n n,表示点数。

接下来 n − 1 n-1 n1 行,给出 u , v , w u,v,w u,v,w ,分别表示树上的 u u u 点和 v v v 点有连边,边的权值是 w w w

输出格式

一行,一个整数表示答案。

样例 #1

样例输入 #1

4
1 2 3
2 3 4
2 4 6

样例输出 #1

7

提示

最长异或序列是 1 , 2 , 3 1,2,3 1,2,3,答案是 7 = 3 ⊕ 4 7=3\oplus 4 7=34

数据范围

1 ≤ n ≤ 100000 ; 0 < u , v ≤ n ; 0 ≤ w < 2 31 1\le n \le 100000;0 < u,v \le n;0 \le w < 2^{31} 1n100000;0<u,vn;0w<231

思路

要求两个点之间的所有元素的异或值,设为 i , j i,j i,j两点, 可以变成 i i i到根节点异或 j j j到根节点的异或值。

image-20230726161558142

因此我们可以去求每个点到根节点1的异或值,使用dfs即可,记录在sum中

然后遍历每个点,求当前点到根节点亦或值sum[i]可以异或得到的最大值。

之后就和上面一题一样了

代码

#include <bits/stdc++.h>

using namespace std;

typedef struct edge {
    int to, w;
} edge;

const int N = 1e5 + 10;
int son[N * 31][2], cnt[N], idx;
int sum[N];// 存到根节点到异或值
vector<edge> e[N];
int n;

void dfs(int x, int fa) {
    for (auto [y, w]: e[x]) {
        if (y == fa) continue;
        sum[y] = sum[x] ^ w;
        dfs(y, x);
    }
}

void insert(int t) {
    int x = 0;
    for (int i = 30; i >= 0; i--) {
        int y = t >> i & 1;
        if (!son[x][y]) son[x][y] = ++idx;
        x = son[x][y];
    }
}

int query(int t) {
    int x = 0, res = 0;
    for (int i = 30; i >= 0; i--) {
        int y = t >> i & 1;
        if (son[x][!y]) {
            res += 1 << i;
            x = son[x][!y];
        } else {
            x = son[x][y];
        }
    }
    return res;
}

signed main() {
#ifndef ONLINE_JUDGE
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif
    cin >> n;
    for (int i = 1; i <= n - 1; i++) {
        int x, y, w;
        cin >> x >> y >> w;
        e[x].push_back({y, w});
        e[y].push_back({x, w});
    }
    dfs(1, 0);
    for (int i = 1; i <= n; i++) insert(sum[i]);
    int res = 0;
    for (int i = 1; i <= n; i++) res = max(res, query(sum[i]));
    cout << res << endl;

    return 0;
}

最小异或对

题目描述

https://ac.nowcoder.com/acm/contest/53485/F

给出一个多重集合(元素可以重复的集合),你需要提供以下操作:

  1. ADD x,向多重集合里添加一个元素x,多重集合内元素可以重复
  2. **DEL x,**从多重集合中删除一个元素x,保证要删除的元素一定存在,如果存在多个x则仅删除其中任意1个
  3. QUE,查询集合中的最小异或对的值,即找到集合中任何**两个元素(可以相等)**异或能得到的最小值,保证询问时集合包含的元素数量不少于2个

对于每个QUE操作,你需要输出查询的结果.

以上操作中涉及的操作数x均为非负整数.

1 < = n < = 2 ∗ 1 0 5 , 0 < = x < 2 30 1<=n<=2*10^5 ,0<=x<2^{30} 1<=n<=2105,0<=x<230

思路-01Trie

与最大异或对类似

思路-结论

最小异或对的结论:最小异或对一定为数组排序后相邻两个数的异或值

证明:

a < b < c a<b<c a<b<c,我们现在需要证明最小异或对只可能是ab或者bc

设c与a的第一个不同的位为第x位(从高向低看),则x位上面c一定为1,a一定为0 ,b可以为0或者1

a,b,c在x位置之前的数字都是相同的。

因此在x位上面,ac的这一位一定为1,ab和b^c一定会有一个异或起来在这一位为0,

因此a^c不可能是最小的,也就是一定是相邻两个数异或起来才是最小。

可以使用平衡树进行增删改查操作.

代码

#include <bits/stdc++.h>

#define int long long
using namespace std;

multiset<int> s, res;
int n, x;
string op;

void add() {
    auto it = s.lower_bound(x);
    if (it != s.end()) res.insert(x ^ *it);
    if (it != s.begin()) res.insert(x ^ *prev(it));
    if (it != s.end() && it != s.begin()) res.erase(res.lower_bound(*it ^ *prev(it)));
    s.insert(x);
}

void del() {
    s.erase(s.find(x));
    auto it = s.lower_bound(x);
    if (it != s.end()) res.erase(res.find(x ^ *it));
    if (it != s.begin()) res.erase(res.find(x ^ *prev(it)));
    if (it != s.end() && it != s.begin()) res.insert(*it ^ *prev(it));
}

int que() {
    return *res.begin();
}

signed main() {
#ifndef ONLINE_JUDGE
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif
    cin >> n;
    while (n--) {
        cin >> op;
        if (op == "ADD") {
            cin >> x;
            add();
        } else if (op == "DEL") {
            cin >> x;
            del();
        } else cout << que() << endl;
    }

    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/47829.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【图像处理】使用 OpenCV 将您的照片变成卡通

图像到卡通 一、说明 在当今世界&#xff0c;我们被图像和视频所包围。从社交媒体到广告&#xff0c;图像已成为一种强大的交流媒介。但是你有没有想过&#xff0c;如果你能把你的照片变成卡通会发生什么&#xff1f;想象一下&#xff0c;为您最喜欢的照片创建动画版本&#xf…

CSP 2021入门级 第一轮 题目讲解

A: a进栈&#xff0c;直接出栈&#xff1b;b进栈&#xff0c;直接出栈&#xff1b;c进栈&#xff0c;直接出栈&#xff1b;d进栈&#xff0c;直接出栈&#xff1b;e进栈&#xff0c;直接出栈。 B&#xff1a;全进栈后全出栈。 C&#xff1a;a和b先进栈&#xff0c;然后直接出…

网络安全(黑客)自学建议笔记

前言 网络安全&#xff0c;顾名思义&#xff0c;无安全&#xff0c;不网络。现如今&#xff0c;安全行业飞速发展&#xff0c;我们呼吁专业化的 就职人员与大学生 &#xff0c;而你&#xff0c;认为自己有资格当黑客吗&#xff1f; 本文面向所有信息安全领域的初学者和从业人员…

Spring AOP (面向切面编程)原理与代理模式—实例演示

一、AOP介绍和应用场景 Spring 中文文档 (springdoc.cn) Spring | Home 官网 1、AOP介绍&#xff08;为什么会出现AOP &#xff1f;&#xff09; Java是一个面向对象&#xff08;OOP&#xff09;的语言&#xff0c;但它有一些弊端。虽然使用OOP可以通过组合或继承的方…

使用xtcp映射穿透指定服务

使用xtcp映射穿透指定服务 管理员Ubuntu配置公网服务端frps配置service自启(可选) 配置内网服务端frpc配置service自启(可选) 使用者配置service自启(可选) 通过frp实现内网client访问另外一个内网服务器 管理员 1&#xff09;配置公网服务端frps2&#xff09;配置内网服务端…

ARM汇编中预定义的寄存器和协处理器名称

一、是什么? 预定义的寄存器和协处理器名称,汇编代码中直接使用就可以. # 二、使用步骤 1.引入库 代码如下(示例): .global _start _start:mov r0,#0x18LDR R3,=0x55555555mov r1,#0x18LDR R1,=0x55555555mov r2,#

java对象的强引用,弱引用,软引用,虚引用

前言:java对象在java虚拟机中的生存状态&#xff0c;面试可能会有人问道&#xff0c;了解一下 这里大量引用 《疯狂Java讲义第4版》 书中的内容

⛳ 面向对象面试题

面向对象面试题目录 ⛳ 面向对象面试题&#x1f69c; 一&#xff0c;成员变量&#xff0c;局部变量&#xff0c;类变量存储在内存的什么地方&#xff1f;&#x1f43e; 1.1&#xff0c;类变量&#xff08;静态成员变量&#xff09;&#x1f4dd; 1.2&#xff0c;成员变量⭐ 1.3…

Redis(四)—— Redis基本的事务操作、Redis实现乐观锁

一、Redis基本的事务操作 首先声明&#xff1a; redis的单条命令是保证原子性的&#xff08;回想一下setnx k1 v1 k5 v5命令如果k1已经存在&#xff0c;那么k5也会设置失败&#xff09;但是redis的事务不保证原子性&#xff01;见下面“1.2 某条命令有错怎么办&#xff1f;”…

Ueditor 百度强大富文本Springboot 项目集成使用(包含上传文件和上传图片的功能使用)简单易懂,举一反三

Ueditor 百度强大富文本Springboot 项目集成使用 首先如果大家的富文本中不考虑图片或者附件的情况下&#xff0c;只考虑纯文本且排版的情况下我们可以直接让前端的vue来继承UEditor就可以啦。但是要让前端将那几个上传图片和附件的哪些功能给阉割掉&#xff01; 然后就是说如…

SpringMvc+Mybatis完整项目

0目录 1.SpringmybatisSpringmvc查询功能&#xff08;记录数&#xff09; 2.查询所有 3.增删改查&#xff08;根据id&#xff09; 4.增加用户注册登录功能 1.SpringmybatisSpringmvc增删改查 新建数据库 创建工程 配置web.xml 配置applicationContext.xml 实体类 My…

SpringBoot 配置⽂件

1.配置文件作用 整个项⽬中所有重要的数据都是在配置⽂件中配置的&#xff0c;⽐如&#xff1a; 数据库的连接信息&#xff08;包含⽤户名和密码的设置&#xff09;&#xff1b;项⽬的启动端⼝&#xff1b;第三⽅系统的调⽤秘钥等信息&#xff1b;⽤于发现和定位问题的普通⽇…

EMP-SSL: TOWARDS SELF-SUPERVISED LEARNING IN ONETRAINING EPOCH

Recently, self-supervised learning (SSL) has achieved tremendous success in learning image representation. Despite the empirical success, most self-supervised learning methods are rather “inefficient” learners, typically taking hundreds of training epoch…

MySQL高可用之MHA集群

目录 一、MHA概述 1.1 什么是 MHA 1.2 MHA 的组成 1.3 MHA 的特点 二、MySQL MHA搭建准备 2.1 实验思路 2.2 实验准备 MHA一主两从高可用集群示意图&#xff1a; 三、搭建 MySQL MHA 3.1 配置主从复制 1、四台服务器都关闭防火墙 2、修改 Master、Slave1、Slave2 节…

汇编调用C语言定义的全局变量

在threadx移植中&#xff0c;系统的systick通过了宏定义的方式定义&#xff0c;很难对接库函数的时钟频率&#xff0c;不太利于进行维护 所以在C文件中自己定义了一个systick_Div的变量&#xff0c;通过宏定义方式设定systick的时钟频率 在汇编下要加载这个systick分频系数 …

STM32 串口基础知识学习

串行/并行通信 串行通信&#xff1a;数据逐位按顺序依次传输。 并行通信&#xff1a;数据各位通过多条线同时传输。 对比 传输速率&#xff1a;串行通信较低&#xff0c;并行通信较高。抗干扰能力&#xff1a;串行通信较强&#xff0c;并行通信较弱。通信距离&#xff1a;串…

在Windows server 2012上使用virtualBox运行CentOS7虚拟机,被强制休眠(二)

问题场景 本月7月10日处理了一个虚拟机被强制暂停的问题&#xff0c;详见&#xff1a;在Windows server 2012上使用virtualBox运行CentOS7虚拟机&#xff0c;被强制暂停当时是由于C盘存储空间不足&#xff0c;导致虚拟机被强制暂停&#xff0c;将虚拟机迁移后&#xff0c;问题…

Mybatis-plus 配置自定义sql(.xml文件)查询语句的步骤

这是使用Mybatis-plus 的自动生成实体类代码生成.xml文件&#xff0c; 所以他会在java目录下&#xff0c;不在resources目录下 如果在java目录下的xml文件&#xff0c;需要分别配置application.yml和pom.xml文件 application.yml 文件进行以下配置&#xff1a; mybatis-plus…

【C刷题】矩阵相等判断与序列中删除指定的数字

目录 BC105-矩阵相等判断 方法1:两矩阵输入完毕后&#xff0c;进行比较 方法2:在接收过程中直接比较 BC98 - 序列中删除指定的数字 方法1:把要删除的元素改为0 方法2:打印不用删除的元素 方法3:定义两个下标 i 和 j(动图演示) 此篇文章是关于牛客网刷题的做题思路和代码…

Docker的数据管理和Dockerfile的指令

Docker的数据管理 一、Docker数据的概念1、数据卷2、数据卷容器 二、端口映射三、容器互联&#xff08;使用centos镜像&#xff09;四、Docker 镜像的创建1、基于现有镜像创建&#xff08;1&#xff09;首先启动一个镜像&#xff0c;在容器里做修改&#xff08;2&#xff09;然…
最新文章