一、基本概念
Trie(字典树)是一种用于实现字符串快速检索的多叉树结构。Tire的每个节点都拥有若干个字符指针,若在插入或检索字符串时扫描到一个字符c,就沿着当前节点的c字符指针,走向该指针指向的节点。
初始化:
一棵空Trie仅包含一个根节点,该点的字符指针均指向空。
插入:
当需要插入一个字符串S时,我们令一个指针p起初指向根节点。然后,依次扫描S中的每个字符c:
(1)若P的c字符指针指向一个已经存在的节点Q,则令P=Q。
(2)若P的c字符指针指向空,则新建一个节点Q,令P的c字符指针指向Q,然后令P=Q。
当S中的字符扫描完毕时,在当前节点P上标记它是一个字符串的末尾。
检索:
当需要检索一个字符串S在Trie中是否存在时,我们令一个指针P起初指向根节点,然后依次扫描S中的每个字符c:
(1)若P的c字符指针指向空,则说明S没有被插入过Trie,结束检索。
(2)若P的c字符指针指向一个已经存在的节点Q,则令P=Q。
当S中的字符扫描完毕时,若当前节点P被标记为一个字符串的末尾,则说明S在Trie中存在,否则说明S没有被插入过Trie。
二、例题一:P2580 于是他错误的点名开始了
本题可以直接用STL重的map书写,为了锻炼,使用字典树书写。
题面:
代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
using namespace std;
const int maxn=10100;
const int len=50;
int t[maxn*len][26];
int ed[maxn*len];
int tot=1,root=1;
char str[maxn];
void _insert(void)
{
int slen=strlen(str+1);
int p=root;
for(int i=1;i<=slen;i++)
{
int ch=str[i]-'a';
if(!t[p][ch]) t[p][ch]=++tot;
p=t[p][ch];
}
ed[p]=1;
}
int _find(void)
{
int slen=strlen(str+1);
int p=root;
for(int i=1;i<=slen;i++)
{
int ch=str[i]-'a';
if(!t[p][ch]) return 0;
p=t[p][ch];
}
return ed[p]++;
}
int main(void)
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%s",str+1);
_insert();
}
int m;
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%s",str+1);
int pos=_find();
if(pos==0) printf("WRONG\n");
else if(pos==1) printf("OK\n");
else printf("REPEAT\n");
}
return 0;
}
三、例题二:P1481 魔族密码
题面:
代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
using namespace std;
const int maxn=2100;
const int len=75;
int t[maxn*len][26];
int ed[maxn*len];
int tot=1,root=1;
char str[maxn];
void _insert(void)
{
int slen=strlen(str+1);
int p=root;
for(int i=1;i<=slen;i++)
{
int ch=str[i]-'a';
if(!t[p][ch]) t[p][ch]=++tot;
p=t[p][ch];
}
ed[p]=1;
}
int dfs(int p)
{
if(p==0) return 0;
int maxx=0;
for(int i=0;i<26;i++)
{
maxx=max(maxx,dfs(t[p][i])+ed[p]);
}
return maxx;
}
int main(void)
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%s",str+1);
_insert();
}
printf("%d\n",dfs(root));
return 0;
}