在词典中获取以片段开头/包含/结尾的单词

时间:2015-03-27 10:12:52

标签: c++ trie suffix-tree prefix-tree

假设我们列出了英语词典中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)情况下的子串匹配中跟踪每个单词的单个树

任何指针?

2 个答案:

答案 0 :(得分:0)

您可以尝试aho corasick算法。自动机实际上是一个trie,而失败函数是trie的广度优先遍历。

答案 1 :(得分:0)

正如我在评论中指出的那样,前缀和后缀情况由通用子字符串情况(#2)涵盖。根据定义,所有前缀和后缀也都是子串。所以我们要解决的是一般的子串问题。

由于您有一个静态字典,因此可以相对容易地将其预处理为快速查询子字符串的表单。你可以使用后缀树来做到这一点,但是构建和处理简单的排序数据平面向量要容易得多,这就是我在这里所描述的内容。

最终目标是获得一个已排序的子词列表,以便可以进行二进制搜索以找到匹配项。

首先,观察一下为了找到与查询片段匹配的最长子串,没有必要列出每个单词的所有可能的子串,而只列出所有可能的后缀;这是因为所有子串都可以被认为是后缀的前缀。 (知道了吗?第一次遇到它时,它有点弯曲,但最后很简单,非常有用。)

因此,如果您生成每个字典单词的所有后缀,然后对它们进行排序,您就足以在任何字典单词中找到任何特定的子字符串:对后缀进行二元搜索以找到下限({ {1}}) - 以查询片段开头的第一个后缀。然后找到上限(std::lower_bound) - 这将是以查询片段开头的最后一个后缀之后的一个。范围内的所有后缀[lower,upper [必须从查询片段开始,因此这些后缀最初来自的所有单词都包含查询片段。

现在,显然拼写出所有后缀会占用大量内存 - 但你并不需要。后缀可以被认为仅仅是单词的索引 - 后缀开始的偏移量。因此,每个可能的后缀只需要一对整数:一个用于(原始)单词索引,一个用于该单词中后缀的索引。 (您可以根据字典的大小巧妙地将这两者组合在一起,以节省更多空间。)

总而言之,您需要做的就是:

  1. 为所有单词生成所有单词后缀索引对的数组。
  2. 根据它们的语义含义(后缀)(不是数值)对它们进行排序。我建议使用自定义比较器std::upper_bound。这是最长的一步,但它可以一次离线完成,因为你的字典是静态的。
  3. 对于给定的查询片段,找到已排序后缀索引中的下限和上限。此范围中的每个后缀对应于匹配的子字符串(查询的长度,从单词索引处的单词的后缀索引开始)。请注意,某些单词可能会匹配多次,并且匹配甚至可能会重叠。

  4. 为了澄清,这里是一个由单词"臭鼬"组成的字典的小例子。和"奶酪"。

    "臭鼬"的后缀是"臭鼬"," 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"在此示例中),没有匹配项;在这种情况下,下限和上限将相等。