[关闭]
@Gary-Ying 2018-01-29T05:51:52.000000Z 字数 2813 阅读 1347

Trie树(字典树)

编程 数据结构


Description
小明有很多单词(全部由小写字母组成,不会有重复单词),现在他想统计以某个字符串为前缀的单词数量(单词本身也是自己的前缀)。

Input
输入n,然后n行每行一个单词,长度不超过10,这n个单词构成字典库
接下来m,然后m行每行一个单词前缀(1<=n<=100000,1<=m<=50000)
Output
对于每个单词前缀,问字典库中以它为前缀的单词有多少个

一些简单的算法

首先,这道题暴力枚举的时间复杂度是O(nm*len),很明显回超时。那么怎么办呢?
观察样例,发现排序后,具有相同前缀的单词会在一起,并且,排序后的序列具有单调性,使我们可以通过二分答案法来解决这道题,具体步骤如下:

  1. 输入字符串,快速排序 (时间复杂度 O(nlgn*len) )。
  2. 二分查找每一个前缀的在序列中第一个出现的单词的位置,即为loc1 (时间复杂度 O(m*lgn*len) )。
  3. 二分查找每一个前缀的在序列中最后一个出现的单词的位置,即为loc2 ( 时间复杂度同 2. )。
  4. loc2-loc1+1即为所求的答案。注意判断不存在的情况,可以通过特判或初值定不同来区分。

Trie树(字典树)

引入

算法的名字很好地概括了算法的核心思想,那么什么是字典树的?
大家都知道字典中的单词是按字典序排列的,现在假设有4个单词 a,ab,abc,abd
如果用普通的char类型来存储,一共需要9个char,但是,我们觉得有点浪费,浪费在哪呢?
a
ab
abc
abd
我们发现这四个单词的第一个字母都是a,对于后面3个单词,它们都有ab的前缀。如果用一棵树来存储这些单词,会不会省空间呢?

思想及时间复杂度分析

如图:

abcdroot1111

如果用一个26叉树来存储上述的单词,我们只需要5个节点就可以了,
更棒的是,随着单词数量的增加,单词之间的前缀重复也会越来越多,能够节省的空间也会越来越多。

我们现在来分析一下其的时间复杂度,假设有n个单词,平均长度为len。对于每个单词,都会访问(创建)len个节点,所以时间复杂度可以认为是O(n*len)

代码( Only Pascal )

只有 Pascal,C++的同学可以使用Map头文件Easiest地解决。

  1. root:=1; nodenum:=1; //初始化 root表示树的根 nodenum表示节点数量
  2. readln(s);
  3. len:=length(s); //s的长度
  4. ins(root,1);
  5. procedure ins(root:longint; s:string); //trie的非递归版本插入
  6. var
  7. now,son,i:longint;
  8. begin
  9. now:=root;
  10. for i:=1 to length(s) do
  11. begin
  12. son:=ord(s[i])-96;
  13. if trie[now,son]=0 then
  14. begin
  15. inc(nodenum);
  16. trie[now,son]:=nodenum;
  17. trie[nodenum,27]:=1;
  18. end
  19. else inc( trie[ trie[now,son] ,27] );
  20. now:=trie[now,son];
  21. end;
  22. end;
  23. function find_trie(root:longint; s:string):longint; //trie的非递归版本查找
  24. var
  25. now,i,son:longint;
  26. begin
  27. now:=root;
  28. for i:=1 to length(s) do
  29. begin
  30. son:=ord(s[i])-96;
  31. if trie[now,son]=0 then exit(0);
  32. now:=trie[now,son];
  33. end;
  34. exit(trie[now,27]);
  35. end;
  36. procedure ins(u,loc:longint); // trie树的递归版本插入
  37. var son:longint;
  38. begin
  39. if loc>len then exit;
  40. son:=ord(s[loc])-96;
  41. if trie[u,son]=0 then
  42. begin //新建节点
  43. inc(nodenum);
  44. trie[u,son]:=nodenum;
  45. inc(trie[nodenum,27]);
  46. end
  47. else inc(trie[ trie[u,son] ,27]);
  48. ins(trie[u,son],loc+1);
  49. end;

相关题目

最大XOR路径 | 解题报告

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注