前缀匹配在自然语言处理中常见的需求。 假设有一个词典和一个句子,找出句子开始位置处匹配的词典中的单词。
Hash标记法
使用一个HashMap,放入词典中词的同时放入前缀词。例如当插入词典中的hello这个词时,同时插入hello的前缀词。
map["hello"] = 1
map["hell"] = 0
map["hel"] = 0
map["he"] = 0
map["h"] = 0
这样查找遇到0的时候继续追加字符进行查找。
传统的Trie树
使用utf8编码,每一字节有256中可能的后缀。
TrieNode {
char ch;
TrieNode children[256];
}
这种方式在汉字处理有很多空间浪费。
字节分割法
传统的Tire树只能存储英文。如果是汉字,可以按字节来分割。例如utf8编码有3个字节,我们没半个字节分一个数出来,那么一个汉字专为6个字母。
Hash基础的Trie
孩子结点放入一个哈希表中。
TrieNode {
char ch;
Map<char,TrieNode*> children;
}
缺点是序列化不太方便,占用内存大。
二分数组基础的Trie
孩子结点排序,然后进行二分查找。
TrieNode {
char ch;
Array<TrieNode> children;
}
二分查找可能没有其他方式快。插入时也要移动内存。
Double Array Trie
整个trie被存储为base和check两个数组。相当于将子结点数组压缩到一个单个数组中。
DoubleArrayTrie容易序列化,但构建速度较慢。
Radix Trie
大概意思是将只有一个子结点和父节点合并,来压缩trie树。
Aho-Corasick
这是一个自动状态机算法,用在多模式匹配。类似与KMP算法的思路,在匹配失败的情况下转移到远的结点。t