字典树问题与AC自动机

    Trie树,又称字典树,单词查找树或者前缀树,是一种用于快速检索的多叉树结构,如英文字母的字典树是一个26叉树,数字的字典树是一个10叉树。字典树的核心就是空间换时间,空间消耗大,但是插入和查询有着很优秀的时间复杂度,利用字符串的公共前缀来避免无谓的字符串比较,降低查询时间。

    Trie树的平均高度h为单词平均长度len,所以Trie树的查询复杂度为O(h)=O(len)

字典树问题与AC自动机_第1张图片

字典树节点:

      每个节点对应一个最大可储存字符数组。假设字典只存26个小写英文字母,那么每个节点下应该有一个长度为最大26的数组。换言说,可存的元素类型越多,单个节点占用内存越大。如果用字典树储存汉字,那么每个节点必须为数千个常用汉字开辟一个数组作为储存空间,占用的内存实在不是一个数量级。不过Trie树就是一种用空间换时间的数据结构。

3个重要性质:

(1) 根节点不包含字符,除根节点外每一个节点都只包含一个字符。
(2) 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
(3) 每个节点的所有子节点包含的字符都不相同。

优点:

  1. 插入和查询的效率很高,均是O(m),其中 m 是待插入/查询的 字符串长度 。

  2. Trie树中不同的关键字不会产生冲突。

  3. Trie树中只有在允许一个关键字关联多个值的情况下才有类似hash碰撞发生。

  4. Trie树不用求hash值,对短字符串有更快的速度。通常,求hash值也是需要遍历字符串的(与hash函数相关)

  5. Trie树可以对关键字 按照字典序排序 (先序遍历)。

     关于查询,有人会说hash表时间复杂度是O(1)不是更快?但是哈希搜索的效率取决于哈希函数的好坏,若一个坏的hash函数导致了很多冲突,效率不一定比Trie树高。

缺点:

  1. 当hash函数很好时,Trie树的查找效率低于哈希搜索。

  2. 空间消耗大。

应用:

1.查询某个字符串出现的次数

每个节点的count置为0,直到这个字符串结束,在末尾处count++.这样,就记录了该字符串的出现次数。

(当数据量大时,必须用字典树,数据量小时,可采用map来做)

具体可参考:https://blog.csdn.net/niushuai666/article/details/6695503

2.查询某个字符串特定序列出现的次数(统计前缀次数)。

每个节点的count初始化为0,当读入一个字符,则count++。这样,查询时,这个节点count记录的就是从头结点到该结点特定序列出现的次数。可以用于统计单词的前缀一类的题目。

其中1,2都可以采用hash表来做。

3.实现热门搜索的排名

对(1)中的trie进行先序遍历,将字符串和出现次数存进一个结构体,最后对这个数组进行快速排序,时间复杂度为O(nlogn),看网上说可以利用分治+trie+最小堆

4.实现自动补全功能

其实原理都差不多,把字符串结尾处的结点当作root,进行先序遍历,即可得出所有以输入的字符串为前缀的答案

https://blog.csdn.net/nk_test/article/details/47836119

https://blog.csdn.net/xiaxzhou/article/details/74905652

https://blog.csdn.net/hihozoo/article/details/51248823

https://www.cnblogs.com/haoxiaozi/p/6318814.html

有关结点的描述:https://blog.csdn.net/a987073381/article/details/52167019

-------------------------------------------------------------------------------------------------------------------------------

AC自动机

KMP算法是单模式串的字符匹配算法,AC自动机是多模式串的字符匹配算法。

例如:我们给出5个单词,say,she,shr,he,her。给定字符串为yasherhs。问多少个单词在字符串中出现过。

AC自动机的构造:

1.构造一棵Trie,作为AC自动机的搜索数据结构。

2.构造fail指针,使当前字符失配时跳转到具有最长公共前后缀的字符继续匹配。如同 KMP算法一样, AC自动机在匹配时如果当前字符匹配失败,那么利用fail指针进行跳转。由此可知如果跳转,跳转后的串的前缀,必为跳转前的模式串的后缀并且跳转的新位置的深度(匹配字符个数)一定小于跳之前的节点。所以我们可以利用 bfs在 Trie上面进行 fail指针的求解。

3.扫描主串进行匹配。

struct node{
    node *next[26];
    node *fail;
    int sum;
};

参考链接:https://blog.csdn.net/niushuai666/article/details/7002823

                https://maples.me/algorithm/2016/04/09/Aho-Corasick-automaton/


你可能感兴趣的:(算法,数据结构)