字典树
又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。
一、原理
对cat,cash,app,apple,aply,ok 建一颗字典树,建成之后如下图所示
1、字典树用边表示字母
2、有相同前缀的单词公用前缀节点,那我们可以的得出每个节点最多有26个子节点(在单词只包含小写字母的情况下)
3、整棵树的根节点是空的。便于查找和插入
4、每个单词结束的时候用一个特殊字符表示,那么从根节点到任意一个‘’所经过的边的所有字母表示一个单词。
二、操作
1.插入单词
从图中可以直观看出,从左到右扫这个单词,如果字母在相应根节点下没有出现过,就插入这个字母;否则沿着字典树往下走,看单词的下一个字母。
我们设数组trie[i][j]=k,表示编号为i的节点的第j个孩子是编号为k的节点。
这里有2种编号,一种是i,k表示节点的位置编号,这是相对整棵树而言的;另一种是j,表示节点i的第j的孩子,这是相对节点i而言的。
我们就按输入顺序对其编第一种号,红色表示编号结果。因为先输入的cat,所以c,a,t分别是1,2,3,然后输入的是cash,因为c,a是公共前缀,所以从s开始编,s是4,以此类推。
注意这里相同字母的编号可能不同
第二种编号,相对节点的编号,紫色表示编号结果。
因为每个节点最多有26个子节点,我们可以按他们的字典序从0——25编号,也就是他们的ASCLL码-a的ASCLL码。
注意这里相同字母的编号相同
代码:
void insert(char *s,int rt)
{
for(int i=0;s[i];i++)
{
int x=s[i]-'a';
if(trie[rt][x]==0)//现在插入的字母在之前同一节点处未出现过
{
trie[rt][x]=tot++;//字母插入一个新的位置,否则不做处理
}
rt=trie[rt][x];//为下个字母的插入做准备
}
/*isw[rt]=true;标志该单词末位字母的尾结点,在查询整个单词时用到*/
}
查找
bool find(char *s,int rt)
{
for(int i=0;s[i];i++)
{
int x=s[i]-'a';
if(trie[rt][x]==0)return false;//以rt为头结点的x字母不存在,返回0
rt=trie[rt][x];//为查询下个字母做准备
}
return true;
}
总模板:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define maxn 2000010
using namespace std;
int tot=1,n;
int trie[maxn][26];
//bool isw[maxn];查询整个单词用
void insert(char *s,int rt)
{
for(int i=0;s[i];i++)
{
int x=s[i]-'a';
if(trie[rt][x]==0)//现在插入的字母在之前同一节点处未出现过
{
trie[rt][x]=tot++;//字母插入一个新的位置,否则不做处理
}
rt=trie[rt][x];//为下个字母的插入做准备
}
/*isw[rt]=true;标志该单词末位字母的尾结点,在查询整个单词时用到*/
}
bool find(char *s,int rt)
{
for(int i=0;s[i];i++)
{
int x=s[i]-'a';
if(trie[rt][x]==0)return false;//以rt为头结点的x字母不存在,返回0
rt=trie[rt][x];//为查询下个字母做准备
}
return true;
//查询整个单词时,应该return isw[rt]
}
char s[22];
int main()
{
tot=0;
int rt=1;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
cin>>s;
insert(s,rt);
}
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
cin>>s;
if(find(s,rt))printf("YES\n");
else printf("NO\n");
}
return 0;
}