浅谈Trie树算法(c++)

news2024/9/21 0:33:22

文章目录

  • 于是他错误的点名开始了
    • 题目背景
    • 题目描述
    • 输入格式
    • 输出格式
    • 样例 #1
      • 样例输入 #1
      • 样例输出 #1
    • 提示
    • 思路
    • AC代码
  • 01Trie
    • 求n个数两两异或的最大值
      • AC代码
    • Nikitosh 和异或
      • 思路
      • AC代码
    • The XOR-longest Path
      • 思路
      • AC代码

又称字典树,用边来代表字母,而从根结点到树上某一结点的路径就代表了一个字符
串。举个例子,1->4->8->12 表示的就是字符串 caa。
很简洁、自然的算法,所以也没什么能讲的,下面直接看一道例题及其代码。
在这里插入图片描述
字典树最基础的应用——查找一个字符串是否在“字典”中出现过。

于是他错误的点名开始了

题目背景

XS中学化学竞赛组教练是一个酷爱炉石的人。

他会一边搓炉石一边点名以至于有一天他连续点到了某个同学两次,然后正好被路过的校长发现了然后就是一顿欧拉欧拉欧拉(详情请见已结束比赛 CON900)。

题目描述

这之后校长任命你为特派探员,每天记录他的点名。校长会提供化学竞赛学生的人数和名单,而你需要告诉校长他有没有点错名。(为什么不直接不让他玩炉石。)

输入格式

第一行一个整数 n n n,表示班上人数。

接下来 n n n 行,每行一个字符串表示其名字(互不相同,且只含小写字母,长度不超过 50 50 50)。

n + 2 n+2 n+2 行一个整数 m m m,表示教练报的名字个数。

接下来 m m m 行,每行一个字符串表示教练报的名字(只含小写字母,且长度不超过 50 50 50)。

输出格式

对于每个教练报的名字,输出一行。

如果该名字正确且是第一次出现,输出 OK,如果该名字错误,输出 WRONG,如果该名字正确但不是第一次出现,输出 REPEAT

样例 #1

样例输入 #1

5  
a
b
c
ad
acd
3
a
a
e

样例输出 #1

OK
REPEAT
WRONG

提示

  • 对于 40 % 40\% 40% 的数据, n ≤ 1000 n\le 1000 n1000 m ≤ 2000 m\le 2000 m2000
  • 对于 70 % 70\% 70% 的数据, n ≤ 1 0 4 n\le 10^4 n104 m ≤ 2 × 1 0 4 m\le 2\times 10^4 m2×104
  • 对于 100 % 100\% 100% 的数据, n ≤ 1 0 4 n\le 10^4 n104 m ≤ 1 0 5 m≤10^5 m105

upd 2022.7.30 \text{upd 2022.7.30} upd 2022.7.30:新增加一组 Hack 数据。

思路

大概的思想就是对所有名字建 trie,再在 trie 中查询字符串是否存在、是否已经点过名,第一次点名时标记为点过名。
可以从这道题中理解 trie 相对于暴力枚举的优越性:将很多信息放在了一起计算,并且将一些无用的信息直接抛弃掉了。

AC代码

#include<bits/stdc++.h>
using namespace std;
const int N=500010;
char s[60];
int n,m,ch[N][26],tag[N],tot=1;
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%s",s+1);
		int u=1;
		for(int j=1;s[j];j++){
			int c=s[j]-'a';
			if(!ch[u][c])ch[u][c]=++tot;
			u=ch[u][c];
		}
		tag[u]=1;
	}
	scanf("%d",&m);
	while(m--){
		scanf("%s",s+1);
		int u=1;
		for(int j=1;s[j];j++){
			int c=s[j]-'a';
			u=ch[u][c];
			if(!u)break;
		}
		if(tag[u]==1){
			tag[u]=2;
			puts("OK");
		}
		else if(tag[u]==2)
			puts("REPEAT");
		else puts("WRONG");
		
	}
}

01Trie

顾名思义,即字符集只有 0/1 的 Trie 树。
常被用来解决有关异或值的问题。
为什么?
异或有着按位考虑的性质,每一位的贡献是分开的,这与 Trie 树用不同的深度存不同的位的性质是吻合的。
如果要最大化异或值,一定是先最大化其最高位,这有一点贪心的思想。所以如果用
Trie 树从高到低来做,正好吻合了这个贪心的思想。

求n个数两两异或的最大值

1 ≤ N ≤ 1 0 5 , 0 ≤ A i < 2 31 1≤N≤10^5 ,0≤A_i<2^{31} 1N105,0Ai<231将这n个数转成二进制,然后插入到 Trie 树里。
接着再取每个数,在 trie 树上跑一遍,在可能的情况下尽量走与该二进制位不同的方向。
这里用到了贪心的思想,因为二进制下 1xxxxx > 0yyyyy 总是成立的,高位优一定全局优。

AC代码

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
struct node
{
int ch[2];
}a[3000000];
bool num[40];
bool ans[40];
int tot=0;
int f[40];
void insert(int x)
{
memset(num,0,sizeof(num));
for(int i=0;x;i++)
{
num[i]=x&1;
x>>=1;
}
int root=0;
for(int i=30;i>=0;i--)
{
if(!a[root].ch[num[i]])a[root].ch[num[i]]=++tot;
root=a[root].ch[num[i]];
}
}
int find(int x)
{
memset(num,0,sizeof(num));
memset(ans,0,sizeof(ans));
int fh=0;
for(int i=0;x;i++)
{
num[i]=x&1;
x>>=1;
}
int root=0;
for(int i=30;i>=0;i--)
{
if(a[root].ch[num[i]^1])root=a[root].ch[num[i]^1],ans[i]=1;
else root=a[root].ch[num[i]],ans[i]=0;
}
for(int i=0;i<=30;i++)fh+=ans[i]*f[i];
return fh;
}
int main()
{
int n,ans=0,t;
f[0]=1;
for(int i=1;i<=30;i++)f[i]=f[i-1]*2;
scanf("%d%d",&n,&t);
insert(t);
for(int i=2;i<=n;i++)
{
scanf("%d",&t);
ans=max(ans,find(t));
insert(t);
}
cout<<ans;
return 0;
}

Nikitosh 和异或

给定一个含 个元素的数组 ,下标从 开始。请找出下面式子的最大值:
( A [ l 1 ] ⨁ A [ l 1 + 1 ] … … A [ r 1 ] ) + ( A [ l 2 ] ⨁ A [ l 2 + 1 ] … … A [ r 2 ] ) (A[l_1]⨁A[l_1+1]…… A[r_1])+(A[l_2]⨁A[l_2+1]…… A[r_2]) (A[l1]A[l1+1]……A[r1])+(A[l2]A[l2+1]……A[r2])
1 ≤ l 1 ≤ r 1 < l 2 ≤ r 2 ≤ N 1 ≤ l_1 ≤ r_1 < l_2 ≤ r_2 ≤ N 1l1r1<l2r2N
x⨁y, 表示 和 的按位异或。
2 ≤ N ≤ 4 × 1 0 5 2 ≤ N ≤ 4 × 10^5 2N4×105
0 ≤ A i ≤ 1 0 9 0 ≤ A_i ≤ 10^9 0Ai109

思路

首先考虑问题的第一部分,对于一个确定的r任取l, ⨁ i = l r a i ⨁\limits _{i=l}^r a_i i=lrai的最大值是多少?将这个数组计为vl 。
首先区间异或和也可以表示为两个前缀异或和相异或。
这样的话我们只需要对于确定的r,找到 s 0 , s 1 , s 2 … s r − 1 s _0, s_1 , s_2 …s_{ r−1} s0,s1,s2sr1里与 s r s_r sr异或值最大的。
这个问题可以从左到右扫一遍,每次在 trie 里查询 s r s_r sr,接着插入 s r s_r sr就好了。
同样的,我们可以求出对于一个确定的l任取r,最大的异或值是多少。将这个数组计
为vr 。
对vl取前缀max,vr取后缀max ,接着枚举i并统计答案 v l i + v r i + 1 vl_i+vr_{i+1} vli+vri+1就好了。

AC代码

#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
const int N = 4e5 + 10;
int tree[N * 31][2], cnt;
int l[N], r[N], a[N], n;
inline void add(int x, int rt) {
    for (int i = 31; i >= 0; i--) {
        int y = ((x >> i) & 1);

        if (tree[rt][y] == 0)
            tree[rt][y] = ++cnt;

        rt = tree[rt][y];
    }

    return ;
}
inline int query(int x, int rt) {
    int res = 0;

    for (int i = 31; i >= 0; i--) {
        int y = ((x >> i) & 1);

        if (tree[rt][y ^ 1]) {
            res += (1 << i);
            rt = tree[rt][y ^ 1];
        } else
            rt = tree[rt][y];
    }

    return res;
}
int main() {
    scanf("%d", &n);
    memset(tree, 0, sizeof(tree));
    cnt = 0;

    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i]);

    l[0] = 0;
    int x = 0;

    for (int i = 1; i <= n; i++) {
        x ^= a[i];
        add(x, 0);
        l[i] = max(l[i - 1], query(a[i], 0));
    }

    memset(tree, 0, sizeof(tree));
    r[n + 1] = 0;
    x = 0;

    for (int i = n; i >= 1; i--) {
        x ^= a[i];
        add(x, 0);
        r[i] = max(r[i + 1], query(a[i], 0));
    }

    int ans = 0;

    for (int i = 1; i <= n; i++)
        ans = max(ans, l[i] + r[i + 1]);

    printf("%d\n", ans);
    return 0;
}

The XOR-longest Path

给定一棵 n 个点的带权树,求树上最长的异或和路径。
1 ≤ n ≤ 1 0 5 1 ≤ n ≤ 10^5 1n105
, 1 ≤ u , v ≤ n , 0 ≤ w < 2 31 1 ≤ u, v ≤ n, 0 ≤ w < 2^{31} 1u,vn,0w<231

思路

异或的一个很重要的性质:
x ⊕ x = 0。
所以说,如果一个元素,我们对其进行了重复的异或,但是这个重复次数是偶数次,那
么可以视作没有进行异或。
那么,对于树上的一条路径,我们就有了一个极为优美的表示方式:
p a t h ( x , y ) = p a t h ( x , l c a ) ⊕ p a t h ( l c a , y ) = p a t h ( x , r o o t ) ⊕ p a t h ( y , r o o t ) path(x, y) = path(x, lca) ⊕ path(lca, y) = path(x, root) ⊕ path(y, root) path(x,y)=path(x,lca)path(lca,y)=path(x,root)path(y,root)
只要求出每个点到根节点的异或和(通过 dfs 就可以简单实现),问题就转化为第一道
题,求点对的最大异或值。

AC代码

#include <bits/stdc++.h>
using namespace std;
inline int read() {
    char ch = getchar();
    int x = 0;

    while (!isdigit(ch))
        ch = getchar();

    while (isdigit(ch)) {
        x = x * 10 + ch - '0';
        ch = getchar();
    }

    return x;
}
const int N = 1e5 + 3;
struct emm {
    int e, f, v;
} a[2 * N];
int h[N];
int idx = 0;
inline  void con(int x, int y, int l) {
    a[++idx].f = h[x];
    h[x] = idx;
    a[idx].e = y;
    a[idx].v = l;
    a[++idx].f = h[y];
    h[y] = idx;
    a[idx].e = x;
    a[idx].v = l;
    return;
}
int d[N], w[N];
void dfs(int x) {
    for (int i = h[x]; i; i = a[i].f)
        if (!d[a[i].e]) {
            w[a[i].e] = (w[x] ^ a[i].v);
            d[a[i].e] = d[x] + 1;
            dfs(a[i].e);
        }

    return;
}
struct ahh {
    int nxt[2];
} tr[3200003];
int cnt = 0;
int b[33];
inline int get(int x) {
    int j = -1;

    while (x) {
        b[++j] = x & 1;
        x >>= 1;
    }

    return j;
}
inline  void add(int x) {
    memset(b, 0, sizeof(b));
    int j = get(x);
    int k = 0;

    for (int j = 31; j >= 0; --j) {
        if (!tr[k].nxt[b[j]])
            tr[k].nxt[b[j]] = ++cnt;

        k = tr[k].nxt[b[j]];
    }

    return;
}
inline long long find(int x) {
    memset(b, 0, sizeof(b));
    int j = get(x);
    long long now = 0;
    int k = 0;

    for (int j = 31; j >= 0; --j) {
        if (tr[k].nxt[1 - b[j]]) {
            now += (1 << j);
            k = tr[k].nxt[1 - b[j]];
        } else
            k = tr[k].nxt[b[j]];
    }

    return now;
}
int main() {
    int n = read();

    for (int i = 1; i < n; ++i) {
        int u = read(), v = read(), w = read();
        con(u, v, w);
    }

    int s = min(7, n);
    d[s] = 1;
    dfs(s);
    long long ans = 0;

    for (int i = 1; i <= n; ++i)
        add(w[i]);

    for (int i = 1; i <= n; ++i)
        ans = max(ans, find(w[i]));

    cout << ans;
    return 0;
}

这是我的第二十篇文章,如有纰漏也请各位大佬指正
辛苦创作不易,还望看官点赞收藏打赏,后续还会更新新的内容。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《最终幻想14》手游版已获准在中国发行

上个月&#xff0c;有传言称史克威尔和腾讯正在合作开发前者大获成功的MMORPG《最终幻想14》的手机版。Niko Partners分析师丹尼尔艾哈迈德在推特上提到&#xff0c;中国国家新闻出版署已批准发行新一批进口游戏&#xff0c;其中包括《最终幻想14》的手机版&#xff0c;名为《最…

AI智能名片小程序:匹配法则下的粉丝经济新探索

摘要&#xff1a;在数字化时代&#xff0c;企业与消费者之间的互动方式正经历着前所未有的变革。AI智能名片小程序作为这一变革中的新兴产物&#xff0c;不仅重塑了传统商务交流的场景&#xff0c;更在匹配法则的指导下&#xff0c;深刻影响着品牌与粉丝关系的构建与维护。本文…

网络安全数字化转型

1. 背景介绍 在当今数字化浪潮席卷全球的背景下&#xff0c;推行数字化网络安全建设显得尤为迫切与重要&#xff0c;这主要根植于两大核心驱动力&#xff1a;实战挑战的严峻性与行业发展的迫切需求。 1.1. 实战难题的迫切应对 随着信息技术的飞速发展&#xff0c;网络…

全球汽车用粉末涂料市场规划预测:2030年市场规模将接近199亿元,未来六年CAGR为4.3%

一、引言 随着全球汽车行业的持续发展&#xff0c;汽车用粉末涂料作为车辆涂装的重要材料&#xff0c;其市场重要性日益凸显。本文旨在探索汽车用粉末涂料行业的发展趋势、潜在商机及其未来展望。 二、市场趋势 全球汽车用粉末涂料市场的增长主要受全球汽车产量增加、消费者对…

OpenCV||超详细的几何变换

2D图像几何变换的33矩阵&#xff1a; 图像常见的几何变换&#xff1a; 图像来源&#xff1a;《OpenCV 4.5计算机视觉开发实战&#xff1a;基于Python》作者&#xff1a;朱文伟 李建英&#xff1b; 1. 平移&#xff08;Translation&#xff09; 在OpenCV中&#xff0c;平移不是…

如果你感到焦虑、精神内耗,那就跑步去吧!

点击上方△腾阳 关注 转载请联系授权 你好&#xff0c;我是腾阳。 深夜里&#xff0c;你是否辗转反侧、思绪万千却难以入眠&#xff1f; 面对工作截止日期压力山大、心力交瘁&#xff0c;但总提不起神&#xff0c;工作效率低下&#xff1f; 人际交往中&#xff0c;被误解和…

windows 下使用MSYS2编译ffmpeg

1. 下载ffmpeg ,最新源码下载FFmpeghttps://ffmpeg.org/ 2.下载MSYS2,并安装(正常操作步骤) MSYS2Software Distribution and Building Platform for Windowshttps://www.msys2.org/3. 安装好MSYS2后,配置编译环境 打开mingw64.e

Python 进度条:告别枯燥等待,让你的程序动感十足!

在日常编程中&#xff0c;我们经常会遇到需要处理耗时任务的情况&#xff0c;例如文件下载、数据处理等等。看着程序运行&#xff0c;却只能干巴巴地等待&#xff0c;实在令人心焦。 别担心&#xff01;今天就来教你如何使用 Python 创建炫酷的进度条&#xff0c;告别枯燥等待…

董明珠:格力正在开发“不要电”的空调!

8月2日&#xff0c;格力电器在河北召开“格力冰洗生活电器战略发布会”。格力电器董事长兼总裁董明珠在现场发表讲话&#xff0c;透露格力正在开发一个“不要电”的空调。据悉&#xff0c;2012年格力就开始开发这个技术。 董明珠表示&#xff0c;现在的光伏发电需要通过逆变器…

横屏无水印风景视频素材去哪里找啊?横屏无水印素材库分享

在进行视频创作和编辑时&#xff0c;拥有高质量的横屏无水印风景视频素材非常关键&#xff0c;尤其是当你打造旅行记录、自然纪录片或是背景视频等内容时。这类素材不仅能显著提升你的作品的视觉效果&#xff0c;还能加强其感染力。然而&#xff0c;许多创作者在寻找合适的风景…

每日学术速递8.4

1.Sparse vs Contiguous Adversarial Pixel Perturbations in Multimodal 标题&#xff1a; Models: An Empirical Analysis 多模态模型中的稀疏与连续对抗性像素扰动&#xff1a;实证分析 作者&#xff1a; Cristian-Alexandru Botocan, Raphael Meier, Ljiljana Dolamic 文…

eslint配置忽略目录和文件

本部分选自《基于vite构建vue3开发环境——eslint整合》。 默认配置下&#xff0c;咱们引入第三方的svg库js文件也会被eslint检查&#xff1a; 在eslint.config.js中增加配置&#xff1a; 注意 这里不能写成./src/assets&#xff0c;除了忽略整个目录&#xff0c;也可以通过通配…

golang命名异常 error var Xxx should have name of the form ErrFoo (ST1012) 解决方法

异常原因分析 这个提示通常发生在我们使用var 定义一个变量来接收 error异常信息时的变量名称不规范所致。 其实这里的异常信息“ error var Xxx should have name of the form ErrFoo (ST1012)” 这个提示里面也已经提醒了" 变量 Xxx 的名称格式应该为 ErrFoo ", …

网络编程相关

关于ipv4和v6 ipv4小细节-------公网和私有地址 端口 InetAddress 协议 UDP、TCP UDP通信程序 发送&#xff08;单播&#xff09;&#xff1a; 接收&#xff08;单播&#xff09;&#xff1a; UDP三种通信方式 单播和广播代码几乎相同&#xff0c;就是将&#xff1a; InetAddr…

第17课 Scratch入门篇:时钟

时钟 故事背景&#xff1a; 在一个遥远的科技星球上&#xff0c;时间对于居民们来说无比珍贵。这个星球上的居民们都是技术高手&#xff0c;他们使用先进的编程技术来管理自己的生活。然而&#xff0c;星球上的时间系统最近出现了故障&#xff0c;导致时间的流逝变得不稳定。为…

【Material-UI 组件】 Autocomplete中的 Free Solo 模式详解

文章目录 一、组件概述1.1 Free Solo 的定义1.2 适用场景 二、基础用法2.1 实现 Free Solo 模式2.2 注意事项 三、高级配置3.1 增强用户体验的设置3.2 可创建项 四、最佳实践4.1 性能优化4.2 可访问性4.3 处理非字符串选项 五、总结 Free Solo 模式允许用户输入任意值&#xff…

将本地微服务发布到docker镜像

描述 将本地springboot微服务发布到docker镜像中并启动容器 第一步 先本地idea创建一个简单的springboot服务&#xff0c;不需要连接数据库相关操作&#xff0c;只包含简单的接口功能做验证。 相关测试代码如下所示 package com.itwopqq.booting;import org.springframewor…

手写高斯牛顿求解非线性最小二乘问题

容易写错的地方&#xff1a; 注意你的residual定义是 z-h(x)&#xff0c; 还是 h(x) - z&#xff0c;这会影响到Hxb的符号。&#xff08;自己推一遍就知道了&#xff09;&#xff0c;我的习惯性定义是z-h(x) class GaussianNewtonOptimizer {// Observation: [x, y]// y std…

C语言 | Leetcode C语言题解之第318题最大单词长度乘积

题目&#xff1a; 题解&#xff1a; int maxProduct(char ** words, int wordsSize){int masks[wordsSize];memset(masks, 0, sizeof(masks));for(int i 0; i < wordsSize; i) {int len strlen(words[i]);for(int j 0; j < len; j) {masks[i] | 1 << (words[i]…