在字符串匹配问题中,如在n个字符串里查找某个字符串,如果用暴力搜索,需要逐个匹配每个字符串,复杂度为O(nm),效率十分低。
在这里可以使用trie树,也叫字典树,其原理相当于翻字典,如需要查找某个单词,需要先找到其开头字母,再找到其第二个字母,查找任意单词只需要查找这个单词中的字母个数次即可。
具体实现
(图片源自网络)
int trie[1001][26];//树
int trie_end[1001] = { 0 };//标记尾节点
int tot = 1;//节点数
int p;//读取位置
具体方法如图,首先定义一个二维数组来模拟字典树,trie[n][26],n为将要存储的大小,26便对应每个字母,如区分大小写则为52。
例如我们需要将abc这个字符串存进字典树里,则我们需要先判断根节点有没有a这个字母,在表达字典树的时候使用trie[p][q] = tot,其中tot为下一个新建节点,p为当前的节点,q便为存储字母的下标。如果根节点有a这个字母,那么直接访问trie[0][0]得到a指向的节点,使p = trie[0][0]即可访问下一个节点,直到访问到最后一个字母即用end数组来标记,具体代码操作如下:
void insert(char* str)
{
p = 0;
for (int i = 0; str[i]; i++)
{
int n = str[i] - 'a'; //用来当作字母下标
if (trie[p][n] == 0)
trie[p][n] = ++tot; //如果节点中没有这个字母则新建节点
p = trie[p][n];
trie_end[p]++; //end数组用来标记节点结束,++可以记录高度
}
}
查找操作
字典树被创建的本身便是用于方便查找,查找的方法也和建树类似,逐个访问节点,如果读取的字符串在字典树里没有到达最后一个字母时候便达到尾节点则没有这个字符串。
int search(char* str)
{
p = 0;
for (int i = 0; str[i]; i++)
{
int n = str[i] - 'a';
if (trie[p][n] == 0)
return 0;
p = trie[p][n];
}
return trie_end[p];
}
总结
字典树方便了字符串的匹配查找,尤其是数据量庞大的时候越为明显,字典树具体有以下常见的应用:
- 字符串检索。检索,查询功能是字典树的基本功能。
- 词频统计。统计一个单词出现了多少次。
- 字符串排序。在插入的时候,在数的平级按字母表的顺序插入。字典树建好之后,用先序遍历,就得到了字典树的排序。
- 前缀匹配。字典树是按公共前缀来建树的,很适合用于搜索提示。例如Linux的行命令,输入一个命令的前面几个字符,系统会自动补全命令后面的字符。
- 应用于AC自动机。
总体来说字典树在字符串匹配方面很简单很高效,代码也比较简洁,也容易理解。