讲解——Trie树(字典树)
Trie树(字典树)
一、引入
字典是干啥的?查找字的。
字典树自然也是起查找作用的。查找的是啥?单词。
看以下几个题:
1、给出n个单词和m个询问,每次询问一个单词,回答这个单词是否在单词表中出现过。
答:简单!map,短小精悍。
好。下一个
2、给出n个单词和m个询问,每次询问一个前缀,回答询问是多少个单词的前缀。
答:map,把每个单词拆开。
judge:n<=200000,TLE!
这就需要一种高级数据结构——Trie树(字典树)
二、原理
在本篇文章中,假设所有单词都只由小写字母构成
对cat,cash,app,apple,aply,ok 建一颗字典树,建成之后如下图所示
由此可以看出:
1、字典树用边表示字母
2、有相同前缀的单词公用前缀节点,那我们可以的得出每个节点最多有26个子节点(在单词只包含小写字母的情况下)
3、整棵树的根节点是空的。为什么呢?便于插入和查找,这将会在后面解释。
4、每个单词结束的时候用一个特殊字符表示,图中用的‘$’,那么从根节点到任意一个‘$’所经过的边的所有字母表示一个单词。
三、基本操作
A、insert,插入一个单词
1.思路
从图中可以直观看出,从左到右扫这个单词,如果字母在相应根节点下没有出现过,就插入这个字母;否则沿着字典树往下走,看单词的下一个字母。
这就产生一个问题:往哪儿插?计算机不会自己选择位置插,我们需要给它指定一个位置,那就需要给每个字母编号。
我们设数组trie[i][j]=k,表示编号为i的节点的第j个孩子是编号为k的节点。
什么意思呢?
这里有2种编号,一种是i,k表示节点的位置编号,这是相对整棵树而言的;另一种是j,表示节点i的第j的孩子,这是相对节点i而言的。
不理解?看图
还是单词cat,cash,app,apple,aply,ok
我们就按输入顺序对其编第一种号,红色表示编号结果。因为先输入的cat,所以c,a,t分别是1,2,3,然后输入的是cash,因为c,a是公共前缀,所以从s开始编,s是4,以此类推。
注意这里相同字母的编号可能不同
第二种编号,相对节点的编号,紫色表示编号结果。
因为每个节点最多有26个子节点,我们可以按他们的字典序从0——25编号,也就是他们的ASCLL码-a的ASCLL码。
注意这里相同字母的编号相同
实际上每个节点的子节点都应该从0编到——25,但这样会发现许多事根本用不到的。比如上图的根节点应该分出26个叉。节约空间,用到哪个分哪个。
这样编号有什么用呢?
回到数组trie[i][j]=k。 数组trie[i][j]=k,表示编号为i的节点的第j个孩子是编号为k的节点。
那么第二种编号即为j,第一种编号即为i,k
2、代码
- void insert()//插入单词s
- {
- len=strlen(s);//单词s的长度
- root=;//根节点编号为0
- for(int i=;i<len;i++)
- {
- int id=s[i]-'a';//第二种编号
- if(!trie[root][id])//如果之前没有从root到id的前缀
- trie[root][id]=++tot;//插入,tot即为第一种编号
- root=trie[root][id];//顺着字典树往下走
- }
- }
B、search,查找
查找有很多种,可以查找某一个前缀,也可以查找整个单词。
再次我们以查找一个前缀是否出现过为例讲解
1、思路
从左往右以此扫描每个字母,顺着字典树往下找,能找到这个字母,往下走,否则结束查找,即没有这个前缀;前缀扫完了,表示有这个前缀。
2、代码
- bool find()
- {
- len=strlen(s);
- root=;//从根结点开始找
- for(int i=;s[i];i++)
- {
- int x=s[i]-'a';//
- if(trie[root][x]==) return false;//以root为头结点的x字母不存在,返回0
- root=trie[root][x];//为查询下个字母做准备,往下走
- }
- return true;//找到了
- }
3、如果是查询某个单词的话,我们用bool变量 v[i]表示节点i是否是单词结束的标志。
那么最后return的是v[root],所以在插入操作中插入完每个单词是,要对单词最后一个字母的v[i]置为true,其他的都是false
4、如果是查询前缀出现的次数的话,那就在开一个sum[],表示位置i被访问过的次数,
那么最后return的是sum[root],插入操作中每访问一个节点,都要让他的sum++
这里前缀的次数是标记在前缀的最后一个字母所在位置的后一个位置上。
比如:前缀abc出现的次数标记在c所在位置的后一个位置上,
四、完整代码
1、查询是否出现
- /*
- trie tree的储存方式:将字母储存在边上,边的节点连接与它相连的字母
- trie[rt][x]=tot:rt是上个节点编号,x是字母,tot是下个节点编号
- */
- #include<cstdio>
- #include<iostream>
- #include<algorithm>
- #include<cstring>
- #define maxn 2000010
- using namespace std;
- int tot=,n;
- int trie[maxn][];
- //bool isw[maxn];查询整个单词用
- void insert(char *s,int rt)
- {
- for(int i=;s[i];i++)
- {
- int x=s[i]-'a';
- if(trie[rt][x]==)//现在插入的字母在之前同一节点处未出现过
- {
- trie[rt][x]=++tot;//字母插入一个新的位置,否则不做处理
- }
- rt=trie[rt][x];//为下个字母的插入做准备
- }
- /*isw[rt]=true;标志该单词末位字母的尾结点,在查询整个单词时用到*/
- }
- bool find(char *s,int rt)
- {
- for(int i=;s[i];i++)
- {
- int x=s[i]-'a';
- if(trie[rt][x]==)return false;//以rt为头结点的x字母不存在,返回0
- rt=trie[rt][x];//为查询下个字母做准备
- }
- return true;
- //查询整个单词时,应该return isw[rt]
- }
- char s[];
- int main()
- {
- tot=;
- int rt=;
- scanf("%d",&n);
- for(int i=;i<=n;i++)
- {
- cin>>s;
- insert(s,rt);
- }
- scanf("%d",&n);
- for(int i=;i<=n;i++)
- {
- cin>>s;
- if(find(s,rt))printf("YES\n");
- else printf("NO\n");
- }
- return ;
- }
数组模拟
2、查询前缀出现次数
- #include<iostream>
- #include<cstring>
- #include<cstdio>
- #include<algorithm>
- using namespace std;
- int trie[][],len,root,tot,sum[];
- bool p;
- int n,m;
- char s[];
- void insert()
- {
- len=strlen(s);
- root=;
- for(int i=;i<len;i++)
- {
- int id=s[i]-'a';
- if(!trie[root][id]) trie[root][id]=++tot;
- sum[trie[root][id]]++;//前缀保存
- root=trie[root][id];
- }
- }
- int search()
- {
- root=;
- len=strlen(s);
- for(int i=;i<len;i++)
- {
- int id=s[i]-'a';
- if(!trie[root][id]) return ;
- root=trie[root][id];
- }//root经过此循环后变成前缀最后一个字母所在位置
- return sum[root];
- }
- int main()
- {
- scanf("%d",&n);
- for(int i=;i<=n;i++)
- {
- cin>>s;
- insert();
- }
- scanf("%d",&m);
- for(int i=;i<=m;i++)
- {
- cin>>s;
- printf("%d\n",search());
- }
- }
- 数组模拟
数组模拟
- #include<cstdio>
- #include<cstring>
- #include<iostream>
- #include<algorithm>
- using namespace std;
- char s[];
- int n,m;
- bool p;
- struct node
- {
- int count;
- node * next[];
- }*root;
- node * build()
- {
- node * k=new(node);
- k->count=;
- memset(k->next,,sizeof(k->next));
- return k;
- }
- void insert()
- {
- node * r=root;
- char * word=s;
- while(*word)
- {
- int id=*word-'a';
- if(r->next[id]==NULL) r->next[id]=build();
- r=r->next[id];
- r->count++;
- word++;
- }
- }
- int search()
- {
- node * r=root;
- char * word=s;
- while(*word)
- {
- int id=*word-'a';
- r=r->next[id];
- if(r==NULL) return ;
- word++;
- }
- return r->count;
- }
- int main()
- {
- root=build();
- scanf("%d",&n);
- for(int i=;i<=n;i++)
- {
- cin>>s;
- insert();
- }
- scanf("%d",&m);
- for(int i=;i<=m;i++)
- {
- cin>>s;
- printf("%d\n",search());
- }
- }
指针写法
五、模板题
hud 1251 统计难题 http://acm.hdu.edu.cn/showproblem.php?pid=1251
codevs 4189 字典 http://codevs.cn/problem/4189/
讲解——Trie树(字典树)的更多相关文章
- 剑指Offer——Trie树(字典树)
剑指Offer--Trie树(字典树) Trie树 Trie树,即字典树,又称单词查找树或键树,是一种树形结构,是一种的单词.对于每一个单词,我们要判断他出没出现过,如果出现了,求第一次出现在第几个位 ...
- AC自动机——1 Trie树(字典树)介绍
AC自动机——1 Trie树(字典树)介绍 2013年10月15日 23:56:45 阅读数:2375 之前,我们介绍了Kmp算法,其实,他就是一种单模式匹配.当要检查一篇文章中是否有某些敏感词,这其 ...
- Trie(字典树)
没时间整理了,老吕又讲课了@ @ 概念 Trie即字典树,又称单词查找树或键树,是一种树形结构,是一种哈希树的变种,典型应用是统计和排序大量的字符串(不限于字符串) Trie字典树主要用于存储字符串, ...
- 9-11-Trie树/字典树/前缀树-查找-第9章-《数据结构》课本源码-严蔚敏吴伟民版
课本源码部分 第9章 查找 - Trie树/字典树/前缀树(键树) ——<数据结构>-严蔚敏.吴伟民版 源码使用说明 链接☛☛☛ <数据结构-C语言版>(严蔚 ...
- [LintCode] Implement Trie 实现字典树
Implement a trie with insert, search, and startsWith methods. Have you met this question in a real i ...
- Trie树|字典树(字符串排序)
有时,我们会碰到对字符串的排序,若采用一些经典的排序算法,则时间复杂度一般为O(n*lgn),但若采用Trie树,则时间复杂度仅为O(n). Trie树又名字典树,从字面意思即可理解,这种树的结构像英 ...
- Trie - leetcode [字典树/前缀树]
208. Implement Trie (Prefix Tree) 字母的字典树每个节点要定义一个大小为26的子节点指针数组,然后用一个标志符用来记录到当前位置为止是否为一个词,初始化的时候讲26个子 ...
- Trie树/字典树题目(2017今日头条笔试题:异或)
/* 本程序说明: [编程题] 异或 时间限制:1秒 空间限制:32768K 给定整数m以及n个数字A1,A2,..An,将数列A中所有元素两两异或,共能得到n(n-1)/2个结果,请求出这些结果中大 ...
- Trie树(字典树)的介绍及Java实现
简介 Trie树,又称为前缀树或字典树,是一种有序树,用于保存关联数组,其中的键通常是字符串.与二叉查找树不同,键不是直接保存在节点中,而是由节点在树中的位置决定.一个节点的所有子孙都有相同的前缀,也 ...
- Trie树 - 字典树
1.1.什么是Trie树 Trie树,即字典树,又称单词查找树或键树,是一种树形结构.典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计.它的优点是最大限 ...
随机推荐
- Hudson安装配置、部署应用及分析
一.部署环境 机器:一台linux虚机,内存1G,操作系统CentOS release 5.6,硬盘100G. 实验应用:乐学方舟后台部署(非正式环境) 应用环境:apache-tomcat-7.0. ...
- RabbitMq install on Centos6.3
安装服务(root) step 1: 启用EPEL:EPEL是一个Fedora Project 推出的 EPEL(Extra Packages for Enterprise Linux),EPEL是 ...
- 错误与修复:ASP.NET无法检测IE10,IE11,导致_doPostBack未定义JavaScript错误,恒处于F5卷动条位置
<browsers> <browser id="IE11" parentID="Mozilla"> <identifica ...
- 取xml文件转成List<T>对象的两种方法
读取xml文件转成List<T>对象的两种方法(附源码) 读取xml文件转成List<T>对象的两种方法(附源码) 读取xml文件,是项目中经常要用到的,所以就总结一下,最 ...
- D11
=-=感觉今天的题目好难... 主要是没有碰到过,所以会觉得不懂怎么写.. 其实现在想想,T1,T2,T3其实都好水..T1其实没有做过还真不会,有做过的话就是个大水题了 T2找最小环..超级裸的,但 ...
- 【又长见识了】函数传参,params参数,ref和out参数详解
一.原来函数这样传参 先看一个函数和函数调用. static void Main(string[] args) { ; Test(num);//局部变量在使用之前赋值 //Test(10); //直接 ...
- vim插件:显示树形目录插件NERDTree安装 和 使用
下载和配置 NERDTree插件的官方地址如下,可以从这里获取最新的版本 https://github.com/scrooloose/nerdtree 下载zip安装包 或者使用下面官网源文件安装方法 ...
- kAudioSessionProperty_AudioCategory 的设置
iPhone上面有两种播放外音的模式:听筒模式和话筒模式,听筒当然是打电话时用的,那个声音当然很小了, 但是开放中,ios默认的就是这种模式,所以在播放外应的时候要加代码重新设置下,如下: ...
- jQuery图片切换插件jquery.cycle.js
Cycle是一个很棒的jQuery图片切换插件,提供了非常好的功能来帮助大家更简单的使用插件的幻灯功能 下载cycle插件并引入,此时,注意把引入它的代码放在引入jQuery主文件之后. <he ...
- PHP:执行模型和内存模型
PHP:执行模型和内存模型 背景 对于任何一种语言,了解其执行模型和内存模型都是有意义的,本文中的内容不见得正确,请多批评. 执行模型 每个请求都是一个独立的PHP进程,两个请求之间会完全隔离,会话和 ...