字典树Trie
字典树又叫单词查找树(Trie)或前缀树(可见刘汝佳《算法竞赛入门经典训练指南》P208)。顾名思义它是与单词的前缀相关的。给你一个单词和一个字典构成的字典树,你可以在O(m)(m为所给单词的长度)时间内判断出该单词是否属于字典。但是如果你用KMP或其他暴力方法,你至少需要把字典中所有单词都遍历一遍。
下图是一个具有单词abc,abcd,b,bcd,efg,hix的字典树。
下面给出字典树的静态模板,所谓静态是因为我们已经实现申请好了足够的节点来存放整个字典树。这个“足够的节点”需要我们估计到底需要多少。
当然字典树也有用指针实现的动态版本,动态版本方便删除字典树中的单词,但是每次构建完字典树如果你已经不需要当前这棵树了,你要记得递归的删除字典树的每个指针,否则内存泄露。
我们用vector<26元素数组> ch;这种形式也可以实现动态的字典树版本,且不用手动删除指针回收内存。
字典树在判断一个单词是否属于字典时很有优势,但是字典树在判断一个文本包含多少个字典中的单词时就显得不足了,因为对于文本串的每次字符都必须从字典树的跟节点走一遍字典树(很类似于模式串匹配时用的暴力方法)。此时应该用AC自动机(有关AC自动机,下个总结就是它。类似于模式串匹配时用的KMP算法)。
字典树静态模板:
#define MAX 26
const int maxnode=4000*100+100;//预计字典树最大节点数目
const int sigma_size=26;//每个节点的最多儿子数
struct Trie
{
//这里ch用vector<26元素的数组> ch;实现的话,可以做到动态内存
int ch[maxnode][sigma_size];//ch[i][j]==k表示第i个节点的第j个儿子是节点k
int val[maxnode];//val[i]==x表示第i个节点的权值为x
int sz;//字典树一共有sz个节点,从0到sz-1标号
//初始化
void clear()
{
sz=1;
memset(ch[0],0,sizeof(ch[0]));//ch值为0表示没有儿子
}
//返回字符c应该对应的儿子编号
int idx(char c)
{
return c-'a';
}
//在字典树中插入单词s,但是如果已经存在s单词会重复插入且覆盖权值
//所以执行insert前需要判断一下是否已经存在s单词了
void insert(char *s)
{
int u=0,n=strlen(s);
for(int i=0;i<n;i++)
{
int id=idx(s[i]);
if(ch[u][id]==0)//无该儿子
{
ch[u][id]=sz;
memset(ch[sz],0,sizeof(ch[sz]));
val[sz++]=0;
}
u=ch[u][id];
}
val[u]=n;
}
//在字典树中查找单词s
bool search(char *s)
{
int n=strlen(s),u=0;
for(int i=0;i<n;i++)
{
int id=idx(s[i]);
if(ch[u][id]==0)
return false;
u=ch[u][id];
}
return val[u];
}
};
Trie trie;//定义一个字典树
字典树应用
HDU 1251 统计难题(字典树Trie):找出所有以字符串s为前缀的单词个数。解题报告!
UVA 1401 Remember the Word(DP+字典树Trie):求一个字符串能有多少种方式通过字典中的单词构成?解题报告!
HDU 1671 Phone List(字典树Trie):问你字典中是否有一个字符串是其他字符串的前缀?解题报告!
HDU 1247 Hat’s Words(字典树Trie):单词匹配。解题报告!
POJ 1056 IMMEDIATEDECODABILITY(字典树Trie):找前缀单词。解题报告!
HDU 4099 Revenge of Fibonacci(高精度加法+字典树Trie):字典树的基本应用,但是涉及大整数,所以需要注意较多细节。解题报告!
POJ 2001 Shortest Prefixes(字典树Trie):输出单词的唯一前缀,如果不存在唯一前缀就直接输出该单词。解题报告!
POJ 2503 Babelfish(字典树Trie):字典树基本应用。解题报告!