基本介绍

字典树又称Trie树,前缀树(事实上前缀树这个名字就很好的解释了Trie的储存方式)。
这是一种树形结构,典型用于统计、排序、和保存大量字符串。所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。
一个字典树的应用场景:在搜索框输入部分单词下面会有一些神关联的搜索内容,你有时候都很神奇是怎么做到的,这其实就是字典树的一个思想。
来一张图理解一下Trie的储存方式:
image.png
字典树有以下几个性质:

  • 根节点不包含字符,除了根节点每个节点都只包含一个字符。可以把root节点理解成一个哨兵节点。
  • 从根节点到某一个叶子节点,路过字符串起来就是该节点对应的字符串。
  • 每个节点的子节点字符不同,也就是找到对应单词、字符是唯一的。

常见操作

我们假定字典树存的只有26个小写英文字母组成的字符串。首先定义Trie树节点结构体:

  1. typedef struct ss
  2. {
  3. int son[27];
  4. } node;
  5. node trie[NMAX]; //存储所有字典树的节点
  6. int total_node = 0; //节点总数(不算根节点)

Trie树两个常见操作:插入和查找(删除实在少见,就不讲了)。

插入

代码如下:

  1. void insert(const char[] str)
  2. {
  3. int pos = 0, len = strlen(str);
  4. for(int i = 0; i < len; i++)
  5. {
  6. if(!trie[pos].son[str[i] - 'a'])
  7. trie[pos].son[str[i] - 'a'] = ++total_node;
  8. pos = trie[pos].son[str[i] - 'a'];
  9. }
  10. }

从根节点开始,我们遍历待插入的字符串字典树 - 图2。对于当前被遍历到的字符字典树 - 图3,我们定位到当前节点字典树 - 图4的子节点在字典树 - 图5节点数组的下标字典树 - 图6,如果下标有效(不为0),那么说明字符串当前字符已经存在,可继续往下遍历,更新字典树 - 图7;否则,则说明字符串当前字符不存在需要插入,给当前树节点字典树 - 图8设置新的子节点位置字典树 - 图9并更新字典树 - 图10

查找

代码如下:

  1. void insert(const char[] str)
  2. {
  3. int pos = 0, len = strlen(str);
  4. for(int i = 0; i < len; i++)
  5. {
  6. if(!trie[pos].son[str[i] - 'a'])
  7. return false;
  8. pos = trie[pos].son[str[i] - 'a'];
  9. }
  10. return true;
  11. }

和插入代码唯一的不同:对于某个当前被遍历到的字符字典树 - 图11,如果当前节点字典树 - 图12对应子节点下标字典树 - 图13为0,说明子节点不存在,即待查找的字符串不存在,直接返回false。

例题

原题链接
AC代码:

  1. #include <iostream>
  2. #include <cstring>
  3. using namespace std;
  4. typedef struct ss
  5. {
  6. int son[27];
  7. int num; //字符串最后一个字符对应节点的num表示被搜索的次数
  8. } node;
  9. node trie[500000];
  10. int total_node = 0;
  11. void insert(const char str[])
  12. {
  13. int pos = 0, len = strlen(str);
  14. for(int i = 0; i < len; i++)
  15. {
  16. if(!trie[pos].son[str[i] - 'a'])
  17. trie[pos].son[str[i] - 'a'] = ++total_node;
  18. pos = trie[pos].son[str[i] - 'a'];
  19. }
  20. }
  21. int search(const char str[])
  22. {
  23. int pos = 0, len = strlen(str);
  24. for(int i = 0; i < len; i++)
  25. {
  26. if(!trie[pos].son[str[i] - 'a']) return 0;
  27. pos = trie[pos].son[str[i] - 'a'];
  28. }
  29. return ++trie[pos].num;
  30. }
  31. int main(void)
  32. {
  33. char str[51];
  34. int n, m;
  35. scanf("%d", &n);
  36. for(int i = 0; i < n; i++)
  37. {
  38. scanf("%s", str);
  39. insert(str);
  40. }
  41. scanf("%d", &m);
  42. for(int i = 0; i < m; i++)
  43. {
  44. scanf("%s", str);
  45. int ans = search(str);
  46. if(ans == 0) printf("WRONG\n");
  47. if(ans == 1) printf("OK\n");
  48. if(ans >= 2) printf("REPEAT\n");
  49. }
  50. return 0;
  51. }