Trie树(字典树)从懵逼到入门
//字典树是个好东西...也是个比较基础的东西,最近经常用到所以写个总结给自己看看...顺便分享一下求大佬指教
1.定义:Trie树
Trie树,即字典树,又称单词查找树或键树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高。
Trie的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。
它有3个基本性质:
- 根节点不包含字符,除根节点外每一个节点都只包含一个字符。
- 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
- 每个节点的所有子节点包含的字符都不相同。
例如,保存”apple”和 “apply”时,由于它们的前四个字母是相同的,所以希望它们共享这些字母,而只对剩下的部分进行分开存储。可以很明显地发现,字母树很好地利用了串的公共前缀,节约了存储空间。
查询操作也非常简单,例如需要查询apple那么只要沿着a-ap-app-appl-apple的路径就可以了
2.应用
(1)串的快速检索
给出N个单词组成的熟词表,以及一篇全用小写英文书写的文章,请你按最早出现的顺序写出所有不在熟词表中的生词。
在这道题中,我们可以用数组枚举,用哈希,用字典树,先把熟词建一棵树,然后读入文章进行比较,这种方法效率是比较高的。
(2)“串”排序
给定N个互不相同的仅由一个单词构成的英文名,让你将他们按字典序从小到大输出用字典树进行排序,采用数组的方式创建字典树,这棵树的每个结点的所有儿子很显然地按照其字母大小排序。对这棵树进行先序遍历即可。
(3)最长公共前缀
对所有串建立字典树,对于两个串的最长公共前缀的长度即他们所在的结点的公共祖先个数,于是,问题就转化为当时公共祖先问题。
3.实现
字典树的插入(Insert)、删除( Delete)和查找(Find )都非常简单,用一个一重循环即可,即第 i 次循环找到前 i 个字母所对应的子树,然后进行相应的操作。实现这棵字典树,我们用最常见的数组保存即可,当然也可以开动态的指针类型。至于结点对儿子的指向,一般有三种方法:
(1)对每个结点开一个字母集大小的数组,对应的下标是儿子所表示的字母,内容则是这个儿子对应在大数组上的位置,即标号;
(2)对每个结点挂一个链表,按一定顺序记录每个儿子是谁;
(3)使用左儿子右兄弟表示法记录这棵树。
三种方法,各有千秋。第一种易实现,但实际的空间要求较大;第二种,
较易实现,空间要求相对较小,但比较费时;第三种,空间要求最小,但相对费时且不易写。但总的来说,几种实现方式都是比较简单的,只要在做题时加以合理选择即可。
4.缺点
字典树的缺点很明显,用大量的空间换取时间,那么显然消耗的内存空间是非常巨大的。
比如小写字母的字典树,每个节点最多可能存在26个子节点,指数级增长的内存消耗
字典树的基本实现:
#include <iostream>
#include<cstdlib>
#define MAX 26
using namespace std; typedef struct TrieNode //Trie结点声明
{ bool isStr; //标记该结点处是否构成单词 struct TrieNode *next[MAX]; //儿子分支
}Trie; void insert(Trie *root,const char *s) //将单词s插入到字典树中
{ if(root==NULL||*s=='\0') return; int i; Trie *p=root; while(*s!='\0') { if(p->next[*s-'a']==NULL) //如果不存在,则建立结点 { Trie *temp=(Trie *)malloc(sizeof(Trie)); for(i=0;i<MAX;i++) { temp->next[i]=NULL; } temp->isStr=false; p->next[*s-'a']=temp; p=p->next[*s-'a']; } else { p=p->next[*s-'a']; } s++; } p->isStr=true; //单词结束的地方标记此处可以构成一个单词
} int search(Trie *root,const char *s) //查找某个单词是否已经存在
{ Trie *p=root; while(p!=NULL&&*s!='\0') { p=p->next[*s-'a']; s++; } return (p!=NULL&&p->isStr==true); //在单词结束处的标记为true时,单词才存在
} void del(Trie *root) //释放整个字典树占的堆区空间
{ int i; for(i=0;i<MAX;i++) { if(root->next[i]!=NULL) { del(root->next[i]); } } free(root);
} int main(int argc, char *argv[])
{ int i; int n,m; //n为建立Trie树输入的单词数,m为要查找的单词数 char s[100]; Trie *root= (Trie *)malloc(sizeof(Trie)); for(i=0;i<MAX;i++) { root->next[i]=NULL; } root->isStr=false; scanf("%d",&n); getchar(); for(i=0;i<n;i++) //先建立字典树 { scanf("%s",s); insert(root,s); } while(scanf("%d",&m)!=EOF) { for(i=0;i<m;i++) //查找 { scanf("%s",s); if(search(root,s)==1) printf("YES\n"); else printf("NO\n"); } printf("\n"); } del(root); //释放空间很重要 return 0;
}
//hdu6059
Kanade's trio
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 255 Accepted Submission(s): 74
There are T test cases.
1≤T≤20
1≤∑n≤5∗105
0≤A[i]<230
For each test case , the first line consists of one integer n ,and the second line consists of n integers which means the array A[1..n]
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define X 33
#define N 600000
#define M 20000000
using namespace std;
int n, a[N], cnt, f[M][3], s[X+3], trans[M];
long long ans_case, ans[X+3][3], g[M][3]; void update(int pos,int x,int modi,int t,int dep)
//当前节点pos,当前位01状态x,add/delete状态modi
//前缀树和后缀树区分t(0/1),层数dep
{ g[pos][t]+=modi;//正常更新当前节点数量 ans[dep][x]+=modi*g[trans[pos]][t^1]; //计算dep层中,后缀树中当前节点为x,前缀树中对应x^1的方案数
}
void add(int x,int t)
{ int j=0, ct=0; while (x) { s[++ct]=x&1; x>>=1; } for (int i=ct+1;i<=X;i++) s[i]=0; for (int i=X;i;i--) { if (!f[j][s[i]]) { f[j][s[i]]=++cnt; if (f[j][s[i]^1]) { trans[cnt]=f[j][s[i]^1]; //记录当前cnt节点在另一棵树中对应的节点编号,用于后面计算答案 trans[f[j][s[i]^1]]=cnt; } } j=f[j][s[i]]; update(j,s[i]^t,1,t,i); }
}
void del(int x,int t)
{ int j=0, ct=0; while (x) { s[++ct]=x&1; x>>=1; } for (int i=ct+1;i<=X;i++) s[i]=0; for (int i=X;i;i--) { j=f[j][s[i]]; update(j,s[i]^t,-1,t,i); }
}
void solve(int x)
{ int j=0, ct=0; while (x) { s[++ct]=x&1; x>>=1; } for (int i=ct+1;i<=X;i++) s[i]=0; for (int i=X;i;i--) ans_case+=ans[i][s[i]];
}
int main()
{ int T_T;scanf("%d",&T_T); while (T_T--) { cnt=ans_case=0; scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&a[i]); if (n==1 || n==2){cout<<0<<endl;continue;} //高中生的题目....总有些坑留在这 for (int i=1;i<=n;i++) add(a[i],1); del(a[1],1);add(a[1],0); for (int i=2;i<=n;i++) { del(a[i],1); solve(a[i]); add(a[i],0); } cout<<ans_case<<endl; for (int i=1;i<=X;i++) ans[i][0]=ans[i][1]=0; for (int i=0;i<=cnt;i++) f[i][0]=f[i][1]=g[i][0]=g[i][1]=trans[i]=0; } return 0;
}
Trie树(字典树)从懵逼到入门相关推荐
- C++实现trie tree字典树(附完整源码)
实现trie tree字典树 实现trie tree字典树算法的完整源码(定义,实现,main函数测试) 实现trie tree字典树算法的完整源码(定义,实现,main函数测试) #include ...
- trie(字典树、前缀树)
trie(字典树.前缀树) 1. trie原理 原理 trie树,又被称为字典树.前缀树,是一种高效地存储和查找字符串集合的数据结构. 一般来说,用到trie的题目中的字母要么全是小写字母,要么全是大 ...
- HDU - 5790 Prefix(主席树+字典树)
题目链接:点击查看 题目大意:给出 n 个字符串,再给出 m 次询问,每次询问需要输出区间 [ l , r ] 内的所有字符串有多少个不同的前缀,要求算法强制在线 题目分析:统计字符串的前缀,不难想到 ...
- Algorithm:树结构(二叉树/多路查找树/字典树)的简介、具体结构(FBT/CBT/BST/BBT/Heap/Huffman、B树/B+树/R树、字典树)及其运算(增删查/遍历/旋转)、代码实现
Algorithm:树结构(二叉树/多路查找树/字典树)的简介.具体结构(FBT/CBT/BST/BBT/Heap/Huffman.B树/B+树/R树.字典树)及其运算(增删查/遍历/旋转).代码实现 ...
- Trie(前缀树/字典树)及其应用
from:https://www.cnblogs.com/justinh/p/7716421.html Trie,又经常叫前缀树,字典树等等.它有很多变种,如后缀树,Radix Tree/Trie,P ...
- Trie(字典树/前缀树)
字典树/前缀树 Trie(发音类似 "try")或者说 前缀树(字典树) 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键.这一数据结构有相当多的应用情景,例如自动补完和 ...
- Luogu P2580 于是他错误的点名开始了 Trie树 字典树
字典树裸题.每次插入询问串,查询的时候拿出来直接查,信息保留在节点上. #include <bits/stdc++.h> using namespace std;char s[51]; i ...
- 【数据结构】前缀树/字典树
目录 1.概述 2.代码实现 3.应用 本文参考: LeetCode 208.实现 Trie (前缀树) 1.概述 前缀树又称字典树.Trie 树.单词查找树,是一棵有根树,同时也是一种哈希树的变种, ...
- 基于Tire树(字典树)与倒排索引实现文本词频统计工具
文章目录 文件读写操作 C风格文件读取 C++风格按行读取 C++风格按单词读取 实现文件词频统计工具 英文文章单词的正确分割 基于Trie树实现文件词频统计 基于Trie树实现带倒排索引的文件词频统 ...
- Tired树(字典树)理解与例题
例题:Trie字符串统计 模板 int son[N][26], cnt[N], idx; //idx 当前用到了的结点的下标 // 0号点既是根节点,又是空节点 // son[][]存储树中每个节点的 ...
最新文章
- python中opencv安装_怎么为python安装opencv模块-百度经验
- 泡泡玛特,走出“盲盒”?
- C++ 操作符优先级
- python实习生面试题_大数据分析实习生面试题库
- java ee的小程序_Java EE 8 –为更多设备提供更多应用程序
- 7-160 找完数 (20 分)
- 供应商否认iPhone 12延迟推出传闻 称生产按计划进行
- [转]用C#编写ActiveX控件
- 我用微笑剪辑我的微电影 ---六月实习总结
- [RMI TCP Connection(10.0.20.175:2181)] DEBUG org.apache.zookeeper.ClientCnxn - Got ping response for
- 映射器配置文件和映射器接口
- TCP/IP长连接和短连接
- 一寸照片制作—Photoshop学习(1)
- Oracle查询上周日期sql,Oracle 获取上周一到周末日期的查询sql语句
- 130行Python代码模仿“蚂蚁呀嘿”特效,太魔性了!
- 梦之光芒/黑客小游戏
- 常用设计模式的代码实战教程
- python开发大型游戏_用Python做大型游戏合适吗?
- 51单片机定时器工作方式1、2原理详解
- python用字符串输出月份_python 输出上个月的月末日期实例
热门文章
- vs2019编译的程序在win7环境上运行失败
- pgMP认证,还是再看看吧!
- fn键台式计算机在哪,fn键在哪里
- android sim卡pin,如何设置手机的SIM卡的PIN码?
- matlab interp1 spline,中国大学MOOC: MATLAB/Octave函数interp1中有一个描述插值方法的参数,其中spline表示...
- labview 霍夫曼树_Huffman tree(赫夫曼树、霍夫曼树、哈夫曼树、最优二叉树)
- 【PhotoShop】用图片自带的alpha通道抠图
- 【Element-UI】在vue中将组件调整为英文(国际化)
- linux超进程,linux下查看超线程
- 如何制作移动端静态网页