简介
字典树,也称Trie或字母树,指的是某个字符集合对应的有根数。树的每条边上恰好对应一个字符,每个顶点代表从根到该结点的路径所对应的字符串(将所有经过的边上的字符按顺序连接起来)。有时我们也称Trie上的边为转移,顶点为状态。
上图展示了在一棵空Trie中依次插入abcd abd bcd efg hi后的形态,实心代表单词的末尾结点。
实际上,任意一个线结点所代表的字符串,都是实际字符串集合中某些串的前缀。特别的,根节点代表空串。
操作实现
1.初始化
一棵空Trie仅包含一个根结点,该点的字符指针均指向空
Trie的结点信息可以用几个数组存储,以下代码中ch[u][i]表示结点 u的i字符指针指向的结点,若数组的值为0表示没有这个子节点。
int ch[N][Z] //Z为字符集大小
bool bo[N] //若bo=true则表示从根到该节点经过的边上的字母组成的字符串是实际字符串集合中的元素
2.插入
当需要插入一个字符串S时,我们令一个指针P起初指向根节点。然后,依次扫描S中的每个字符c:
(1) 若指针P 的c字符指针指向一个已存在的结点Q,则令P=Q。
(2) 若指针P的c字符指针指向空,则新建一个结点Q,令P的c字符指针指向Q,然后令P=Q。
(3) 当S中的字符扫描完毕时,在当前结点指针P上标记它是一个字符串的末尾。
现在要对一个字符集为小写英文字母的Trie插入一个字符串S:
int tot=1;
void insert(char *s) //char *s表示一个字符数组
{
int len=strlen(s);
int u=1; //1为根结点
for(int i=0;i<len;i++)
{
int c=s[i]-'a';
if(!ch[u][c])
ch[u][c]=++tot; //若不存在这条边则要新建一个结点与转移边
u=ch[u][c];
}
bo[u]=true; //在串的结尾将bo赋值,表示它代表一个实际字符集合中的元素
}
3.查询
当需要查询一个字符串S在Trie中是否存在时,我们令一个指针P起初指向根结点,然后依次扫描S中的每个字符c:
(1) 若指针P的c字符指针指向空,说明S没有没插入Trie,结束查询
(2) 若指针P的c字符指针指向一个已经存在的结点Q,则令P=Q。
(3) 若指针S中的字符扫描完毕时,若当前结点P被标记为一个字符串的末尾,则说明S在Trie中已经存在,否则说明S没有被插入过Trie
查询一个字符串S是否是给定字符串集合中某个串的前缀:
bool find(char *s)
{
int len=strlen(s);
int u=1;
for(int i=0;i<len;i++)
{
int c=s[i]-'a';
if(!ch[u][c]) return false;
u=ch[u][c];
}
return true;
}