首页 > 其他分享 >字典树学习笔记

字典树学习笔记

时间:2023-08-25 11:12:18浏览次数:36  
标签:color qquad 笔记 学习 插入 scriptsize 节点 字典

字典树

字典树(Trie)简介

又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。
——百度百科

字典树是一种树形结构,用于用于统计,排序和保存大量的字符串。

graph TD A(root) --> B(a) A(root) --> C(w) A(root) --> D(p) B --> E(c) C --> J(h) C --> F(a) D --> G(c) E --> H(e) E --> I(u) J --> K(o)

\[\scriptsize\color{grey}{字典树(Trie)} \]

\(\qquad\qquad\qquad\qquad\qquad\qquad\qquad\)image

\[\scriptsize\color{grey}{字典树(Trie)} \]

字典树概念

假设现在有六个字符串,分别是:“ac”、“ace”、“acu”、“wa”、“who”、“pc”,用这几个字符串构造Trie
\(\qquad\qquad\qquad\qquad\qquad\qquad\qquad\)image

\[\scriptsize\color{grey}{字典树构造} \]

\[\color{red}{注意,其中灰色的点表示该节点是一个字符串结束的位置} \]

字典树的运作(图解)

1.插入(建树)

插入字符串的思路很简单,假设现在我们要在一棵空Trie中按顺序插入“ac”、“ace”、“acu”、“wa”、“who”、“pc”

0.插入前

\(\qquad\qquad\qquad\qquad\qquad\qquad\qquad\)image

\[\scriptsize\color{grey}{一颗空树} \]

1. 插入“ac”

我们发现当前节点(root)没有“a”,于是新建一个节点“a”;同样的,我们也在“a”下新建一个节点“c”,“c”标记为灰色
\(\qquad\qquad\qquad\qquad\qquad\qquad\qquad\)image

\[\scriptsize\color{grey}{插入ac} \]

2. 插入“ace”

我们发现当前节点(root)有“a”,于是经过节点“a”;同样的,我们也经过“a”节点“c”,“c”标记为灰色;我们发现当前节点(“c”)没有“e”,于是新建一个节点“e”,“e”标记为灰色

\(\qquad\qquad\qquad\qquad\qquad\qquad\qquad\)image

\[\scriptsize\color{grey}{插入ace} \]

3. 插入“acu”

我们发现当前节点(root)有“a”,于是经过节点“a”;同样的,我们也经过“a”节点“c”,“c”标记为灰色;我们发现当前节点(“c”)没有“u”,于是新建一个节点“u”,“u”标记为灰色

\(\qquad\qquad\qquad\qquad\qquad\qquad\qquad\)image

\[\scriptsize\color{grey}{插入acu} \]

4. 插入“wa”

我们发现当前节点(root)没有“w”,于是新建一个节点“w”;同样的,我们也在“w”下新建一个节点“a”,“a”标记为灰色

\(\qquad\qquad\qquad\qquad\qquad\qquad\qquad\)image

\[\scriptsize\color{grey}{插入wa} \]

5. 插入“who”

我们发现当前节点(root)有“w”,于是经过节点“w”;我们发现当前节点(“w”)没有“h”,于是新建一个节点“h”;同样的,我们发现当前节点(“w”)没有“o”,于是也新建一个节点“o”,“o”标记为灰色

\(\qquad\qquad\qquad\qquad\qquad\qquad\qquad\)image

\[\scriptsize\color{grey}{插入who} \]

6. 插入“pc”

我们发现当前节点(root)没有“p”,于是新建一个节点“p”;同样的,我们也在“p”下新建一个节点“c”,“c”标记为灰色
\(\qquad\qquad\qquad\qquad\qquad\qquad\qquad\)image

\[\scriptsize\color{grey}{插入pc} \]

全程

\(\qquad\qquad\qquad\qquad\qquad\qquad\qquad\)image

\[\scriptsize\color{grey}{插入全程} \]

\[{\color{80,80,100} {\Large \mathsf{插入(建树)就这样结束了,Trie的插入思路可以总结为一点:\mathbf{如果有就过,没有就新建}} } } \]

2.查找

  • 假设我们要查找“acu”这个字符串,我们需要顺着树边往下找
    \(\qquad\qquad\qquad\qquad\qquad\qquad\)image

\[\scriptsize\color{grey}{字典树查找acu} \]

如果最终的那个点是灰色(该节点是一个字符串结束的位置),查找就完成了,该字符串存在

  • 再假设我们要查找“what”这个字符串
    \(\qquad\qquad\qquad\qquad\qquad\qquad\)image

\[\scriptsize\color{grey}{字典树查找what} \]

当找到“a”时,发现没有“a”,这时可以直接返回没找到,不用继续找下去了!

字典树实现

编码函数:

int num(char x){//编码 
	if(x>='0'&&x<='9')return x-'0';
	else if(x>='A'&&x<='Z')return x-'A'+10;
	else return x-'a'+36;
}

1.构建字典树

不用建结构体,只需开两个数组,一个变量,分别记录每个点每个字母下一个去的点的编号,该点是否是一个字符串结束的位置,点的编号

int nex[100001][30]={0},cnt=0;
bool b[100001];

我们可以通过存储每个点每个字母下一个要去哪个点,这样就可以实现树上的移动了
如果代码不明白可以回顾一下上文插入方法

void insert(string s){
    int p=0;//此点编号 
   	for(int i=0;i<s.size();i++){
      	int x=num(s[i]);//编码 
		if(!nex[p][x])nex[p][x]=++cnt;//没有点就新建 
		p=nex[p][x];//更新所在点 
		b[p]++;//前缀增加 
	}
}  

调用入口:insert(s);

2.查找

按编号向下查找,如果没有这个点就返回没找到,一直找到最后就可以返回该点是否是一个字符串结束的位置
如果代码不明白可以回顾一下上文查找方法

int find(string s){
	int p=0;//此点编号 
	for(int i=0;i<s.size();i++){
		int x=num(s[i]);//编码 
		if(!nex[p][x])return 0;//没有点就返回0 
		p=nex[p][x];//更新所在点 
	}
	return b[p];//返回此点末尾是否存在 
}	

调用入口:find(s);(可以直接cout<<find(s);)

3.结构体封装

int num(char x){//编码(也可以不要)
	if(x>='0'&&x<='9')return x-'0';
	else if(x>='A'&&x<='Z')return x-'A'+10;
	else return x-'a'+36;
}	
struct Trie{
    int nex[100001][30],cnt=0;
	bool b[100001];
    void insert(string s){
    	int p=0;//此点编号 
   		for(int i=0;i<s.size();i++){
      		int x=num(s[i]);//编码 
			if(!nex[p][x])nex[p][x]=++cnt;//没有点就新建 
			p=nex[p][x];//更新所在点 
			if(i==s.size()-1)b[p]=1;//记录末尾 
		}
	}
    int find(string s){
		int p=0;//此点编号 
		for(int i=0;i<s.size();i++){
			int x=num(s[i]);//编码 
			if(!nex[p][x])return 0;//没有点就返回0 
			p=nex[p][x];//更新所在点 
		}
		return b[p];//返回此点末尾是否存在 
	}
};

调用入口:t.insert(s);
调用入口:t.find(s);(可以直接cout<<find(s);)

例题1

luogu P8306 【模板】字典树
板子题,只是查找的目的变了而已

点击查看题目
#include<bits/stdc++.h>
using namespace std;
int nex[3000010][70]={0},cnt=0,c[3000010];
int read(){//快读 
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int num(char x){//编码 
	if(x>='0'&&x<='9')return x-'0';
	else if(x>='A'&&x<='Z')return x-'A'+10;
	else return x-'a'+36;
}
void insert(string s){
    int p=0;//此点编号 
   	for(int i=0;i<s.size();i++){
      	int x=num(s[i]);//编码 
		if(!nex[p][x])nex[p][x]=++cnt;//没有点就新建 
		p=nex[p][x];//更新所在点 
		c[p]++;//前缀增加 
	}
}
int find(string s){
	int p=0;//此点编号 
	for(int i=0;i<s.size();i++) {
		int x=num(s[i]);//编码 
		if(!nex[p][x])return 0;//没有点就返回 
		p=nex[p][x];//更新所在点 
	}
	return c[p];//返回此点前缀
}	
int main(){
	int T,n,q;
	cin>>T;
	while(T--){
		n=read();
		q=read();
		for(int i=0;i<=cnt+10;i++)//初始化 
            for(int j=0;j<70;j++)
                nex[i][j]=0;
        for(int i=0;i<=cnt+10;i++)c[i]=0;
        cnt=0;
		for(int i=1;i<=n;i++){//构建字典树 
			string s;
			cin>>s;
			insert(s);
		} 
		for(int i=1;i<=q;i++){//查找 
			string s;
			cin>>s;
            printf("%d\n",find(s));
		} 
	}
	return 0;
}

总结

字典树是一种很有用的数据结构,它在统计,排序和保存大量的字符串方面有很大优势,此外,它还有其他许多功能,在练习中我们可以发现它的更多用处

题单

  1. luogu P2580 于是他错误的点名开始了 思路:板子题,字符串结束点多一个点名次数标记就行了
  2. luogu P8306 【模板】字典树 思路:板子题,已讲解
  3. luogu P4551 最长异或路径 思路:01trie,把数转化为二进制字符串存入trie中,还要贪心一下

标签:color,qquad,笔记,学习,插入,scriptsize,节点,字典
From: https://www.cnblogs.com/ccr-note/p/trie.html

相关文章

  • Markdown学习
    MarkDown学习标题字体hello,word!**+**hello,word!*+*hello,word!***+***hello,word!~~+~~引用hello,word!>+空格非分线---/***图片超链接名称[我的博客](晓光1004的主页-博客园(cnblogs.com))列表+空格q表格......
  • 并行求解器基础知识学习
      1.数字化工具的新特征    。。。。物理机-->虚拟化-->容器化   2.分布式并行编程基础(1)传相关并行编程框架:MPI(消息传递接口)——一种典型的并行编程框架OpenCL   CUDA(2)HDFS分布式文件系统下的MapReduce并行模式shuffle调度  ......
  • 联邦学习:对“数据隐私保护”和“数据孤岛”困境的破局
    作者:vivo互联网安全团队- TuDaxi随着计算力、算法和数据量的巨大发展,人工智能迎来第3次发展高潮,开始了各行业的落地探索。然而,在“大数据”兴起的同时,更多行业应用领域中是“小数据”或者质量很差的数据。“数据孤岛”现象广泛存在,例如在信息安全领域的应用中,虽然多家企业推出......
  • BeautifulSoup:学习使用BeautifulSoup库进行HTML解析和数据提取。
    BeautifulSoup是一个用于解析HTML和XML文档的Python库。它可以帮助我们从网页中提取数据,并以易于操作的方式进行分析。以下是使用BeautifulSoup进行HTML解析和数据提取的基本语法:安装BeautifulSoup库:首先,你需要在你的Python环境中安装BeautifulSoup库。可以使用以下命令进行安......
  • Linux学习疑惑总结
    重定向问题Linuxshell中2>&1的含义首先了解下1和2在Linux中代表什么,先整理一份在Linux系统中012是一个文件描述符:名称代码操作符Java中表示Linux下文件描述符(Debian为例)标准输入(stdin)0<或<<System.in/dev/stdin->/proc/self/fd/0->/dev/pts/0......
  • abp-vnext-pro 实战(九,前端vue和vben学习)
    vben效果 VbenAdmin(vvbin.cn) 对应的代码在 vue-vben-admin/src/views/demo/page/form/basic/data.tsatmain·vbenjs/vue-vben-admin(github.com){field:'time',component:'RangePicker',label:'起止日期',colProps:{......
  • 《深入理解Java虚拟机》读书笔记:方法调用
      方法调用并不等同于方法执行,方法调用阶段唯一的任务就是确定被调用方法的版本(即调用哪一个方法),暂时还不涉及方法内部的具体运行过程。在程序运行时,进行方法调用是最普遍、最频繁的操作,但前面已经讲过,Class文件的编译过程中不包含传统编译中的连接步骤,一切方法调用在Class文件......
  • 赵老师 计数原理 课程笔记
    计数原理分类加法计数原理与分步乘法计数原理分类加法计数原理引例题干用一个大写的英文字母或一个阿拉伯数字给教室里的一个座位编号,总共能编出多少种不同的号码?解决因为英文字母共有\(26\)个,阿拉伯数字共有\(10\)个,所以总共可以编出\(26+10=36\)种不同的号......
  • Programming abstractions in C阅读笔记:p127-p129
    《ProgrammingAbstractionsInC》学习第51天,p127-p129,总结如下:一、技术总结1.stringlibrary掌握常用函数如strlen,strcpy用法。2.bufferoverflow(缓冲区溢出)(1)什么是buffer?p129,Arraysthatarepreallocatedandlateruseasarepositoryfordatacalledbuffers......
  • Go语言字典(map)的使用
    目录3.字典(map)的使用3.1字典的初始化方式1:3.2字典的初始化方式2:3.3字典的初始化方式3:3.4字典的遍历1:3.5字典的遍历2:3.6判断字典中有无某个key3.7删除字典中的某个键值对3.字典(map)的使用3.1字典的初始化方式1:packagemainimport"fmt"funcmain(){ varscoreM......