这与stackoverflow上的大多数trie问题有点不同(是的,我花时间搜索和阅读),所以请耐心等待。
我有文件A,例如:allow *,apolog *等。总共有成千上万的这样的条目。我的文件B包含一个文本正文,最多可包含数千个单词。我希望能够将文件B中文本中的单词与文件A中的单词匹配。
示例:
文件B的“道歉”将与文件A的“道歉*”匹配
文件B的“a”既不匹配“allow *”也不匹配“apolog *”
文件B的“apologizetomenoworelseiwillkillyou”也会匹配文件A的“道歉*”
有人可以建议一个算法/数据结构(最好是在python中可以做到),这可以帮助我实现这个目标吗?我所研究的尝试似乎更多的是将前缀与整个单词匹配,但在这里,我将整个单词与前缀相匹配。词干算法是不可能的,因为它们有固定的规则,而在这种情况下我的后缀可以是任何东西。我不想在文件A中遍历我的整个列表,因为这会花费太多时间。
如果这令人困惑,我很乐意澄清。感谢。
答案 0 :(得分:1)
将所有前缀放在哈希表中。然后取B中的每个单词并在哈希表中查找它的所有前缀。你得到的任何一击都表示匹配。
因此哈希表包含“allow”和“apolog”。对于“道歉”,你会查找“a”然后“ap”,依此类推,直到你查看“apolog”并找到匹配。
答案 1 :(得分:1)
如果我理解您要查找的内容,您希望能够看到文件A中与文件B中给定的完整单词匹配的所有前缀.Trie数据结构允许您将单个前缀与列表相匹配完整的话,但你需要走另一个方向。
如果是这样,您仍然可以使用trie进行匹配,使用查找表来反转结果。这是算法:
这是实现该算法的一些代码。你需要名为Trie
的trie类和作为参数words
和prefixes
传入的iterables(如果你不希望值同时存在于内存中,则使用生成器):
def matchPrefixes(words, prefixes):
"""Returns a word-to-prefix lookup table."""
trie = Trie()
for word in words:
trie.add(word)
lookupTable = defaultdict(list)
for prefix in prefixes:
matchedWords = trie.matchPrefix(prefix)
for word in matchedWords:
lookupTable[word].append(prefix)
return lookupTable
这在时间和内存方面应该非常有效,特别是当单词列表比前缀列表短得多时。与任何单词不匹配的前缀在针对trie进行检查后将不会使用任何内存。
答案 2 :(得分:1)
如果文件B中的单词数远大于文件A中的前缀,您还可以构建前缀的Trie并匹配其中的单词。
如果您了解Trie的工作方式会更容易。 Trie是一个由字符串构建的树,如下所示。匹配Trie中的字符串是从根到其中一个叶子的过程。
在你的问题中,如果我们将前缀放在Trie中,并查找单词,我们需要将Trie中的一些内部节点标记为前缀的终止。当我们在Trie中查找单词时,每当我们到达标记为前缀终止的节点时,我们将该前缀添加为单词“匹配”。 (然后我们继续写下一封信)。
这正是@ Blckknght解决方案的逆转解决方案。哪个更有效取决于文件A和文件B中的哪一个更大。
在@ Blckknght的解决方案中,Trie中的每个节点都由一组单词(其路径)包含节点标记。搜索前缀以前缀的最后一个字母结束。当它停止时,我们采用搜索停止的Trie节点,然后我们添加节点上标记的与前缀匹配的集合。
如果对任何人都有帮助,我会写一些伪造的代码。
Trie from wiki, from which you can find some code in "Algorithms" part
答案 3 :(得分:1)
假设每个文件中有100,000个单词。
排序= n * log(n) 二进制搜索查找= log(n)
所以这是更糟糕的情况n * log(n)
这是100,000 * log(100,000)= 100,000 * 11 = 10 ^ 6 =几乎是瞬间
我认为你不需要这些小文件。简单地排序和二元搜索:
__author__ = 'Robert'
from bisect import bisect_right
file_a = """hell*
wor*
howard*
are*
yo*
all*
to*""".splitlines()
file_b = """hello world how are you all today too told town""".split()
a_starts = sorted(word[:-1] for word in file_a) #can do this easily if only 100, 000 words as you say.
def match(word):
pos = bisect_right(a_starts, word)
#assert 0 <= pos < len(a_starts)
matched_word = a_starts[pos - 1]
return matched_word if word.startswith(matched_word) else None
for word in file_b:
print(word, " -> ", match(word))
"""
hello -> hell
world -> wor
how -> None
are -> are
you -> yo
all -> all
today -> to
too -> to
told -> to
town -> to
"""