假设我们列出了英语词典中A-Z的所有词典单词。
我有三个案例要对这些单词列表执行:
1)找出" 开头的所有单词"一个特定的片段
eg: If my fragment is 'car', word 'card' should be returned
2)找出所有" 包含"片段作为子串
eg: If my fragment is 'ace', word 'facebook' should be returned
3)找出" 以结尾的所有单词"一个特定的片段
eg: If my fragment is 'age', word 'image' should be returned
在互联网上进行一些搜索练习后,我发现1)可以通过trie /压缩trie完成,3)可以通过后缀树完成。
我不确定如何实现2)。 还有哪些更好的场景可以处理所有这三种情况?由于保留前缀和后缀树可能是一项内存密集型任务。
请告诉我任何其他需要注意的方面。
提前致谢。
PS:我将使用C ++来实现这个目标
编辑1:目前我在这里构建了一个后缀树,并提供了很多帮助。
Single Word Suffix Tree Generation in C Language
在这里,我需要为整个英语词典单词构建一个后缀树。我应该创建一个
a)每个单词的单独后缀树或
b)为所有单词创建一个通用后缀树。
我不确定如何在a)情况下的子串匹配中跟踪每个单词的单个树
任何指针?
答案 0 :(得分:0)
您可以尝试aho corasick算法。自动机实际上是一个trie,而失败函数是trie的广度优先遍历。
答案 1 :(得分:0)
正如我在评论中指出的那样,前缀和后缀情况由通用子字符串情况(#2)涵盖。根据定义,所有前缀和后缀也都是子串。所以我们要解决的是一般的子串问题。
由于您有一个静态字典,因此可以相对容易地将其预处理为快速查询子字符串的表单。你可以使用后缀树来做到这一点,但是构建和处理简单的排序数据平面向量要容易得多,这就是我在这里所描述的内容。
最终目标是获得一个已排序的子词列表,以便可以进行二进制搜索以找到匹配项。
首先,观察一下为了找到与查询片段匹配的最长子串,没有必要列出每个单词的所有可能的子串,而只列出所有可能的后缀;这是因为所有子串都可以被认为是后缀的前缀。 (知道了吗?第一次遇到它时,它有点弯曲,但最后很简单,非常有用。)
因此,如果您生成每个字典单词的所有后缀,然后对它们进行排序,您就足以在任何字典单词中找到任何特定的子字符串:对后缀进行二元搜索以找到下限({ {1}}) - 以查询片段开头的第一个后缀。然后找到上限(std::lower_bound
) - 这将是以查询片段开头的最后一个后缀之后的一个。范围内的所有后缀[lower,upper [必须从查询片段开始,因此这些后缀最初来自的所有单词都包含查询片段。
现在,显然拼写出所有后缀会占用大量内存 - 但你并不需要。后缀可以被认为仅仅是单词的索引 - 后缀开始的偏移量。因此,每个可能的后缀只需要一对整数:一个用于(原始)单词索引,一个用于该单词中后缀的索引。 (您可以根据字典的大小巧妙地将这两者组合在一起,以节省更多空间。)
总而言之,您需要做的就是:
std::upper_bound
。这是最长的一步,但它可以一次离线完成,因为你的字典是静态的。为了澄清,这里是一个由单词"臭鼬"组成的字典的小例子。和"奶酪"。
"臭鼬"的后缀是"臭鼬"," kunk"," unk"," nk"和" k"。表示为索引,它们是std::stable_sort
。 " cheese"的后缀"奶酪"," heese"," eese"," ese"," se"和" ; E&#34 ;.指数为0, 1, 2, 3, 4
。
因为"臭鼬"是我们非常有限的虚构词典中的第一个单词,我们将它指定为索引0." cheese"在索引1.所以最后的后缀是:0, 1, 2, 3, 4, 5
。
对这些后缀进行排序会产生以下后缀字典(我添加了实际的相应文本子字符串仅用于说明):
0:0, 0:1, 0:2, 0:3, 0:4, 1:0, 1:1, 1:2, 1:3, 1:4, 1:5
考虑查询片段" e"。下限是1,因为" e"是第一个大于或等于查询的后缀" e"。上限是4,因为4(" heese")是第一个大于" e"的后缀。因此,1,2和3的后缀都以查询开头,因此它们来自的所有单词都包含查询作为子字符串(在后缀索引处,查询的长度)。在这种情况下,所有这三个后缀属于" cheese",在不同的偏移量。
请注意,对于不是任何单词的子字符串的查询片段(例如" a"在此示例中),没有匹配项;在这种情况下,下限和上限将相等。