Algorithm Aho FSA的三元树vs trie和map作为过渡表

Algorithm Aho FSA的三元树vs trie和map作为过渡表,algorithm,trie,finite-automata,ternary-search-tree,aho-corasick,Algorithm,Trie,Finite Automata,Ternary Search Tree,Aho Corasick,使用三元树的FSA和将转换表实现为搜索树(例如std::map)的trie之间有什么区别?看起来两者都有O(logk)复杂度来读取一个符号和O(S)内存复杂度,其中k是字母表大小,S是所有接受的输入字符串的长度之和 如果我们不需要在运行时更改自动机,那么最好的选择不是使用(符号、状态)转换对的排序向量和二进制搜索吗?三元搜索树(TST)和在每个节点上用二进制搜索树实现的Trie之间没有真正的区别。事实上,您可以将后者视为前者的(低效)实现;TST的优点是易于优化,并且空间开销合理 经典的Trie

使用三元树的FSA和将转换表实现为搜索树(例如std::map)的trie之间有什么区别?看起来两者都有O(logk)复杂度来读取一个符号和O(S)内存复杂度,其中k是字母表大小,S是所有接受的输入字符串的长度之和


如果我们不需要在运行时更改自动机,那么最好的选择不是使用(符号、状态)转换对的排序向量和二进制搜索吗?

三元搜索树(TST)和在每个节点上用二进制搜索树实现的Trie之间没有真正的区别。事实上,您可以将后者视为前者的(低效)实现;TST的优点是易于优化,并且空间开销合理

经典的Trie在决策节点上使用直接查找,并使用符号索引的转换向量。这是
O(1)
时间,但空间需求很大。尽管如此,还是有一些方法可以优化存储。此外,还存在混合解决方案,其中Trie结构仅用于树顶部的宽决策节点;一旦候选数量减少到一定数量,就可以使用快速扫描或哈希表来查找合适的候选

以简单的方式使用(符号、状态)转换的排序向量需要每个转换的
O(logt)
时间,其中
T
是转换的总数;本质上是所有输入字符串的总大小。给定目标的总时间为
| target |*log(T)


相比之下,TST每次转换所需的时间不超过
O(logs)
时间,其中
S
是字母表的大小;这个数字比
T
小得多。此外,整个目标字符串上的查找总数受输入字符串数量的限制,因此整个查找的总和要小于
|target |*log

,考虑到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;

}
资料来源:


为什么你认为向量的大小是O(T)?我看不出在这里使用二进制搜索树有什么区别。“如果自动机是确定性的,那么每个状态的转换就不会超过字母表的大小。”莉莎丽:也许我误解了你的建议。你不是建议对整个树使用一个数组吗?或者你是在为每个州建议一个向量?如果是后者,它在道德上等同于二叉搜索树,所以我真的看不出问题的关键。(例如,在实践中,我的TST实现确实使用了一个向量而不是单个节点,但我认为这是一个实现细节。)我建议为每个状态使用一个向量。就搜索复杂度而言,它相当于二叉搜索树,但我认为它更有效,因为内存中的密集表示。我想知道的是,转换表的排序向量是否比三叉树更有效。也许没有实际测试就很难判断,但我听说三元树被广泛使用,而且向量方法对我来说似乎更有效,只要我们不需要动态结构,所以问题是为什么要使用TST?如果你的字母表有26个字母,向量占用的空间与三元树差不多,假设过渡表的填充量适中。如果您的字母表是unicode,那么向量将是巨大的,而三元树可能仍然很小。不管怎样,获得正确答案的唯一方法是编写两个版本的程序,并在实际数据上进行比较,看看哪个版本在您的特定情况下更好。