首页 > 其他分享 >trie 树详解 + 例题

trie 树详解 + 例题

时间:2024-08-24 15:49:37浏览次数:8  
标签:字符 cnt ch trie int 详解 字符串 例题 节点

这篇做的笔记la~

ほら、もうすぐ晴れますよ!

字典树

字典树(trie 树)是一种用于实现字符串快速检索的多叉树结构。
trie 树的每个节点都拥有若干个字符指针,若在插入或检索字符串时扫描到一个字符 c,就沿着当前节点的 c 字符指针,走向该指针指向的节点。
下图即为一个简易版字典树,存储了单词:abc、bac、abd。

图有点奇怪请见谅

实现:

初始化:

一棵空的 \(trie\) 树只包含一个根节点,该字符的指针均指向空。

不过字符还需要转换成一个数字,这里我们就需要用到一个类似于map映射的东西

struct node{
	int cnt=0; //cnt 表示到这个节点为止,一共有多少个前缀
	int son[65]; // 每个节点的分支
}z[maxn]; 

int getnumber(char ch)//把字符转换成数字
{
	if(ch>='A'&&ch<='Z') return ch-'A';
	if(ch>='a'&&ch<='z') return ch-'a'+26;
	return ch-'0'+52;
 } 

插入:

当需要插入一个字符串 \(s\) 时,我们令一个指针 \(P\) 起初指向根节点。然后依次扫描 \(s\) 中的每一个字符 \(c\)

  • 若 \(P\) 的 \(c\) 字符指针指向一个已经存在的节点 \(Q\) ,则令 \(P=Q\)
  • 若 \(P\) 的 \(c\) 字符指针指向空,则新建一个节点 \(Q\) 令 \(P\) 的 \(c\) 字符指针指向 \(Q\) ,然后令 \(P=Q\)
  • 当 \(s\) 中的字符扫描完毕,在当前节点 \(P\) 上标记它是一个字符串的末尾
void insert(string s) //插入字符串 s
{
	int now=1;//从根节点开始查找 
	for(int i=0;i<s.length();i++) //分解每一个字符
	{
		int num=getnumber(s[i]);
		if(!z[now].son[num]) //从 now 出发没有这个字符
			z[now].son[num]=++cnt;//添加这条边和这条边连向的节点 
        //z[now].cnt++;//到这里有前缀 
	 	now=z[now].son[num];//沿着这条边查找下一个字符 
	} 
	z[now].cnt++; 
	//若已经是字符串的最后一个字符,则代表字典树的这个节点是一个单词的末尾,统计的cnt需要+1
 	return ;
 } 

检索:

当需要检索一个字符串 \(s\) 在 \(Trie\) 中是否存在时,我们令一个指针 \(P\) 起初指向根节点,然后依次扫描 \(s\) 中的每个字符 \(c\)。

  • 若 \(P\) 的 \(c\) 字符指针指向空,则说明 \(S\) 没有被插入到 Trie 树中,结束检索;
  • 若 \(P\) 的 \(c\) 字符指针指向一个已经存在的节点 \(Q\) ,则令 \(P=Q\)
  • 若在当前节点 \(P\) 被标记为一个 字符串的末尾,则说明 \(S\) 在 \(Trie\) 树中存在,否则说明 \(S\) 没有被插入过
void query(string s)
//查找字符串 s 在 trie 中是否存在
{
	int now=1; 
	for(int i=0;i<s.length();i++)
	{
		int num=getnumber(s[i]);
		if(z[now].son[num]==0)
		{
			cout<<0<<endl;//表示不存在
			return; 
		}
		now=z[now].son[num];
	}
	cout<<z[now].cnt<<endl;
	return;
 } 

例题:

P8306 【模板】字典树

这个输入我看了好久才明白 qwq

  • 题目意思:
    给你 \(n\) 个字符串 \(si\),再给你 \(q\) 次询问,每次询问给你一个字符串 \(ti\),求出 \(ti\) 上面 \(n\) 个 \(si\) 中多少个的前缀。

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

  • 思路:
    让每个节点的 cnt 表示到达这个节点的字符串数量,每次询问查找 \(ti\) 的最后一个字符所在的节点的 cnt 值。
  • 代码:
#include<bits/stdc++.h>

using namespace std;

int T;
int n,q;
int cnt,start; 

struct node{
	int son[65];//大小写敏感
	int cnt;//表示到达这个点的字符串有多少 
}z[3000100]; 

int getnumber(char ch)
{
	if(ch>='a'&&ch<='z')return ch-'a'+1; 
    //小写字母占据 1~26 的数组范围
	else if(ch>='A'&&ch<='Z')return ch-'A'+27;
    //大写字母占据 27~52 的数组范围
	else return ch-'0'+53; 
    //数字占据 53~62 的数组范围
}

void insert(string s)
{
	int now=start;
	for(int i=0;i<s.length();i++)
	{
		int num=getnumber(s[i]);
		if(!z[now].son[num])
			z[now].son[num]=++cnt;
		z[now].cnt++;
		now=z[now].son[num];
	}
	z[now].cnt++;
	return;
}

void query(string t)
{
	int now=start;
	for(int i=0;i<t.length();i++)
	{
		int num=getnumber(t[i]);
		if(!z[now].son[num]){
			cout<<0<<endl;
			return;
		}
		now=z[now].son[num];
	}
	cout<<z[now].cnt<<endl;
	return;
}

int main()
{
	cin>>T;
	while(T--) //因为有多组测试数据
	//每次查询完都做一下清空太麻烦了
	//我们可以每次重新选一个root,那就用原来的节点数 +1 
	{
		cin>>n>>q;
		for(int i=1;i<=n;i++)
		{
			string s;
			cin>>s;
			insert(s);
		}
		for(int i=1;i<=q;i++)
		{
			string t;
			cin>>t;
			query(t); 
		 }
		 start=++cnt; 
	}
 } 

P10470 前缀统计

  • 题目意思:
    这个题意思挺清晰的,就不重复了。
  • 思路:
    其实就是把上一个题目倒过来了?
    那我们就换个实现方法。
    其实只需要改动一个地方:cnt 的含义。
    我们可以把所有的 \(si\) 放入 \(Trie\) 中,每个字符串的末尾节点的cnt+1,这样 cnt 的含义变成:有多少个 \(si\) 在这里结尾
    每次查询时,答案就把 \(Ti\) 所经过的所有节点的 cnt 值加起来。
  • 代码:
#include<bits/stdc++.h>

using namespace std;

int n,m;
int cnt=1; //我也不知道为啥这里要是 1

struct node{
	int son[30];//只有小写 
	int cnt;//表示到这个点结束的字符串有多少 
}z[1000100]; 

int getnumber(char ch)
{
	return ch-'a'+1;
}

void insert(string s)
{
	int now=1;
	for(int i=0;i<s.length();i++)
	{
		int num=getnumber(s[i]);
		if(!z[now].son[num])
			z[now].son[num]=++cnt;
		//z[now].cnt++;//这里就不需要加了 
		now=z[now].son[num];
	}
	z[now].cnt++;
	return;
}

void query(string t)
{
	int ans=0;
	int now=1;
	for(int i=0;i<t.length();i++)
	{
		int num=getnumber(t[i]);
		if(!z[now].son[num]){
			cout<<ans<<endl;
			return;
		}
		now=z[now].son[num];
		ans+=z[now].cnt;
	}
	cout<<ans<<endl;
	return;
}

int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		string s;
		cin>>s;
		insert(s);
	}
	for(int i=1;i<=m;i++)
	{
		string t;
		cin>>t;
		query(t);
	}
	return 0;
}

完结撒花! 总算学完了

感觉还挺简单的?

标签:字符,cnt,ch,trie,int,详解,字符串,例题,节点
From: https://www.cnblogs.com/lazy-ZJY0307/p/18376650

相关文章

  • 大话C语言:第46篇 C语言项目工程化之Makefile详解
    1Makefile概述Makefile是一种用于自动化构建和管理程序的工具,以文本文件的形式存在。它主要记录了程序的编译规则、依赖关系和操作指令,使得在开发过程中能够轻松地进行代码的编译、链接和部署。Makefile文件中的命令有一定规范,一旦该文件编写好以后在Linux命令行中执行一条......
  • awk打印除某数据项/某列数/某些列数之外其它列数据的实现以及Twemproxy(redis集群方案
    一、awk打印除某数据项/某列数/某些列数之外其它列数据的实现        偶尔碰到一个需求,我需要使用awk打印数据,但是只需要打印某列之后的其它列,比如我只要第2列及之后的所有数据,如何实现呢?实际很简单:#将$1置成空,然后打印即可awk'{$1="";print}'filepathawk'{$1......
  • MySQL执行计划详解
    Explain语法EXPLAINSELECT……变体:1.EXPLAINEXTENDEDSELECT……将执行计划“反编译”成SELECT语句,运行SHOWWARNINGS可得到被MySQL优化器优化后的查询语句2.EXPLAINPARTITIONSSELECT……用于分区表的EXPLAIN执行计划包含的信息 id包含一组数字,表示查询......
  • MYSQL limit用法详解
    当需要从数据库查询的表有上万条记录的时候,一次性查询所有结果会变得很慢,特别是随着数据量的增加特别明显,这时需要使用分页查询。对于数据库分页查询,也有很多种方法和优化的点。下面简单说一下我知道的一些方法。准备工作为了对下面列举的一些优化进行测试,下面针对已有的一张表......
  • MySQL的Grant命令详解
    MySQL赋予用户权限命令的简单格式可概括为:grant权限on数据库对象to用户 一、grant普通数据用户,查询、插入、更新、删除数据库中所有表数据的权利。grantselectontestdb.*tocommon_user@'%'grantinsertontestdb.*tocommon_user@'%'grantupdateontestd......
  • 莫队详解
    莫队详解一、莫队定义莫队是由2010年信息学国家集训队队员莫涛发明的一种算法,可以将静态离线区间查询的时间复杂度将至\(O(m\sqrt{n})\)下面便是一道莫队例题Lougu1972[SDOI2009]HH的项链虽然这道题莫队过不了,但是确实是很好的一道莫队题。题意:给你一个又\(n\)个......
  • 【网络安全】基础知识详解(非常详细)零基础入门到精通,收藏这一篇就够了
    一、什么是网络安全?百度上对“网络安全”是这么介绍的:“网络安全是指网络系统的硬件、软件及其系统中的数据受到保护,不因偶然的或者恶意的原因而遭受到破坏、更改、泄露、系统连续可靠正常地运行,网络服务不中断。”嗯…是不是感觉有点抽象。那么我们再换一种表述:网......
  • 【C语言初级课程详解】第22课时-C语言结构体
    C 结构体C数组允许定义可存储相同类型数据项的变量,结构是C编程中另一种用户自定义的可用的数据类型,它允许您存储不同类型的数据项。结构体中的数据成员可以是基本数据类型(如int、float、char等),也可以是其他结构体类型、指针类型等。结构用于表示一条记录,假设您想要跟......
  • ES6解构赋值详解;全面掌握:JavaScript解构赋值的终极指南
    目录全面掌握:JavaScript解构赋值的终极指南一、数组解构赋值1、基本用法2、跳过元素3、剩余元素4、默认值二、对象解构赋值1、基本用法2、变量重命名3、默认值4、嵌套解构三、复杂的嵌套结构解构四、函数参数解构赋值1、对象解构作为函数参数2、带有默认值的函......
  • 震撼❗️几乎是跪着读完的一本书❗️ HuggingFace自然语言处理详解,快速掌握HuggingFace这
    今天又来给大家推荐一本HuggingFace的好书,这本《HuggingFace自然语言处理详解》综合性讲解HuggingFace社区提供的工具集datasets和transformers,书中包括最基础的工具集的用例演示,具体的项目实战,以及预训练模型的底层设计思路和实现原理的介绍。通过本书的学习,读者可以快速......