使用三叉树的 FSA 与将转换表实现为搜索树(例如 std::map)的 trie 有什么区别?看起来两者都具有读取一个符号的 O(log k) 复杂度和 O(S) 内存复杂度,其中 k 是字母大小,S 是所有接受的输入字符串的长度之和。
如果我们不需要自动机在运行时更改,那么最好的选择不是使用(符号,状态)转换对的排序向量以及二进制搜索吗?
使用三叉树的 FSA 与将转换表实现为搜索树(例如 std::map)的 trie 有什么区别?看起来两者都具有读取一个符号的 O(log k) 复杂度和 O(S) 内存复杂度,其中 k 是字母大小,S 是所有接受的输入字符串的长度之和。
如果我们不需要自动机在运行时更改,那么最好的选择不是使用(符号,状态)转换对的排序向量以及二进制搜索吗?
三元搜索树 (TST) 和在每个节点上使用二叉搜索树实现的 Trie 之间没有真正的区别。实际上,您可以将后者视为前者的(低效)实现;TST 的优点是易于优化,空间开销合理。
经典的 Trie 在决策节点上使用按符号索引的转换向量直接查找。这是O(1)
时间,但空间需求很大。尽管如此,还是有一些优化存储的方法。此外,还存在混合解决方案,其中 Trie 结构仅用于树顶部的宽决策节点;一旦候选者的数量减少到很小的程度,就可以使用快速扫描或哈希表来找到合适的候选者。
以天真的方式使用(符号,状态)转换的排序向量需要O(log T)
每个转换的时间,其中T
是转换的总数;本质上是所有输入字符串的总大小。给定目标的总时间将是|target|*log(T)
.
相比之下,TSTO(log S)
每次转换只需要时间,其中S
是字母的大小;这是一个比 小得多的数字T
。此外,整个目标字符串的查找总数受到输入字符串数量的限制,因此整个查找的总和小于|target|*log(S)
.
鉴于 Aho-Corasick 的说明方式,
这是我的节点:
public class AhoCorasickNode
{
// This part works as a Trie
public char literal; // c
public String stack; // abc
public AhoCorasickNode previous; // { ab }
public AhoCorasickNode[] next; // { abca }, { abcb }, { abcc }, ..
//-----------------------------
// This part is used when solving
boolean inDictionary;
public AhoCorasickNode suffix;
public AhoCorasickNode dictionarySuffix;
}
资源: