字典树概念
是一种树形结构
Trie树的基本性质可以归纳为:
(1) 根节点不包括字符,除根节点意外每个节点只包含一个字符
(2) 从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串
(3) 每个节点的所有子节点包含的字符串不相同
当然,Trie树也有一个缺点,如果系统中存在大量字符串且这些字符串基本没有公共前缀,则相应的trie树将非常消耗内存
利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。
基本操作有:查找插入删除
应用
- 字符串检索
- 字符串公共前缀
- 排序
- 后缀树,AC自动机
字典树的储存:利用二维数组
如果用一位数组表示,时26^n空间复杂度,如果用二维数组表示,是26*n的空间复杂度,查询时间复杂度为O(1);
对于单个字符tree[root][id];root 表示这个字母在单词的第几项,id表示这个字母的在字母表里的下标
如对于acb
1000
0020
0300
数字表示顺序
对于
1000
0234
0000
表示
ab
ac
ad
sum[1]=3
sum[2]=1;
sum[3]=1;
sum[4]=1;
如果前缀都是一样的话没有问题,但前缀不一样时
对于
ac
cb
103
002
000
040
原来3应该在i=2这一行的j=2,但cb是一个新的数,所以c在第一行
总结
tree[i][j]=z;i轴就是树的深度,j表示字符在字母的下标;z表示顺序
所以得出结论,sum[x]表示的是x的结点数,可以认为以x为子字符串,的字符的个数
对于i=0时,出现了几个数,表示有几个数
例题:
第一题:
HDU1251统计出以某个字符串为前缀的单词数量,先把输入的字符串以字典树的形式插入到二维数组,再用sum去记录子节点数
#include<stdio.h> #include<iostream> #include<string.h> using namespace std; const int maxn =2e6+5; int tree[maxn][30]; int sum[maxn]; int tot; //字典树的插入 void insert_(char *str){ int len=strlen(str); int root=0; for(int i=0;i<len;i++){ int id=str[i]-'a';//把字母用数字表示,方便sum的统计 if(!tree[root][id]) tree[root][id]=++tot;//这个字母存过,不变化 sum[tree[root][id]]++;//记录节点访问次数 root=tree[root][id]; } //root在此对应某个单词,一一对应 } //字典树的查找 int find_(char *str){ int len=strlen(str); int root=0;//初始为0是对于第一个字母肯定在第一行 for(int i=0;i<len;i++){ int id=str[i]-'a'; if(!tree[root][id]) return 0;//如果为空,表示字典里没有这个字符 root=tree[root][id];//之后的肯定不会在第一行,tot的值就是他所在的i位置 } return sum[root];//返回当前字符串结尾节点的访问次数,也就是作为前缀的出现次数 } int main(){ char ss[maxn]; tot=0;//表示从第一个字母开始,后面的字母的id //memset(tree,0,sizeof(tree));全局静态变量,初始值为0,本题只进行一次操作,所以不用初始化 while(gets(ss)){ if(ss[0]=='\0') break;//输入为空时 insert_(ss); } while(scanf("%s",ss)!=EOF){ printf("%d\n",find_(ss)); } return 0; }
HDU2072统计不同单词数
思路:利用字典树记录每个字母,字母之间进行标记
https://blog.csdn.net/qq_38891827/article/details/80532462