【问题标题】:Finding word with longest common prefix in vocabulary在词汇表中查找具有最长公共前缀的单词
【发布时间】:2020-05-15 09:05:22
【问题描述】:

给定一个单词和一个词汇表,我想在词汇表中找到所有具有最长公共前缀的条目。

这里有一些例子:

> vocabulary = {"english", "englishx", "english-indian", "enya", "spanish"}
> prefix, words = find_by_longest_prefix(vocabulary, "englishy")
english: ['englishx', 'english', 'english-indian']
> prefix, words = find_by_longest_prefix(vocabulary, "entomology")
en: ['enya', 'englishx', 'english', 'english-indian']
> prefix, words = find_by_longest_prefix(vocabulary, "spania
spani: ['spanish']

现在想象一下,对于不同的输入词但相同的词汇,您必须多次调用此方法。我的幼稚实现在 O(n) 时间内找到匹配的单词(n 是输入单词的长度),但占用大量内存:O(n*m),其中 n 是词汇的大小,m 是最长的长度词汇表中的单词。

from collections import defaultdict


def build_words_by_prefix(vocabulary):
    words_by_prefix = defaultdict(list)
    for word in vocabulary:
        for i in range(1, len(word) + 1):
            prefix = word[:i]
            words_by_prefix[prefix].append(word)
    return words_by_prefix


def find_by_longest_prefix(vocabulary, word):
    words_by_prefix = build_words_by_prefix(vocabulary)
    for i in range(len(word)+1, 1, -1):
        prefix = word[:i]
        words = words_by_prefix.get(prefix, False)
        if words:
            return prefix, words
    return False

我正在寻找一种既节省内存又节省时间的解决方案。我听说过 tries 可以更有效地存储前缀,但我想知道如何在这里使用它们?

【问题讨论】:

  • 您可以使用名为“trie”的树形结构来表示词汇表。见How to create a trie in Python
  • 非常感谢@Asocia 的讨论。我在问题中添加了一些附加信息。
  • 您可能应该只是将单词放在排序列表中并进行二进制搜索。它会比在 python 中走一条树更快更紧凑。
  • @MattTimmermans 好点。看起来这可以使用内置的 bisect python 来实现:stackoverflow.com/questions/7380629/…

标签: python algorithm


【解决方案1】:

您的方法将提供相对快速的解决方案。在不改变数据结构的情况下,您仍然可以(平均而言)通过用二分搜索替换反向循环来加快速度。平均而言,这将减少查找次数。

如果您需要减少内存消耗,您可以尝试一下,即不将前缀存储为键,而是存储单个字符。嵌套节点将字母作为键,出现在下一个位置,......等等。在有效单词的最后一个字符之后的节点将具有设置为 True 的布尔属性。它仍然可以有嵌套节点,以防单词也有有效的扩展名。

我在这里建议使用比上述更多内存的 Trie 实现,但避免在每次查找时从单个字符构建单词。此 trie 变体中的布尔值被实际单词替换。

class Trie(dict):
    word = ""

    def add(self, *words):
        for word in words:
            node = self
            for ch in word:
                if ch not in node:
                    node[ch] = Trie()
                node = node[ch]
            node.word = word

    def words(self):
        if self.word:
            yield self.word
        for ch in self:
            yield from self[ch].words()

    def longestprefix(self, word):
        node = self
        prefix = word
        for i, ch in enumerate(word):
            if ch not in node:
                prefix = word[:i]
                break
            node = node[ch]
        return (prefix, list(node.words()))

trie = Trie()
trie.add("english", "englishx", "english-indian", "enya", "spanish")
print(trie.longestprefix("englishy"))
print(trie.longestprefix("entomology"))
print(trie.longestprefix("spania"))

【讨论】:

    猜你喜欢
    • 2011-12-23
    • 2018-09-30
    • 2013-07-14
    • 2021-09-11
    • 1970-01-01
    • 1970-01-01
    • 2013-01-25
    • 1970-01-01
    • 2022-11-22
    相关资源
    最近更新 更多