【问题标题】:String matching algorithm : (multi token strings)字符串匹配算法:(多标记字符串)
【发布时间】:2013-12-24 11:03:15
【问题描述】:

我有一本包含大量字符串的字典。每个字符串可以有 1 到 4 个标记(单词)。示例:

字典:

  1. 肖申克的救赎
  2. 教父\
  3. 低俗小说
  4. 黑暗骑士
  5. 搏击俱乐部

现在我有一个段落,我需要弄清楚 para 中有多少个字符串是字典的一部分。 例如,当下面的段落:

肖申克的救赎在 IMDB 前 250 名中被认为是有史以来最伟大的电影。至少有一两年我偶尔会查看 IMDB 前 250 名 肖申克已赎回教父争夺头把交椅。

对字典运行,我应该得到粗体字作为字典的一部分。

我怎样才能用最少的字典调用来做到这一点。

谢谢

【问题讨论】:

  • 字典应该有多大? '我怎样才能用最少的字典调用来做到这一点' 所以它关于查找复杂性,而不是内存使用 - 对吗?编程语言有什么限制吗?
  • 段落如何给出,一个长字符串还是一个文件?
  • 字典有几百万个多标记字符串。是的,查找复杂性是主要问题。没有限制 reg 编程语言。段落将是一团文本
  • Aho-Corasick string matching algorithm 是您想要的。它构建了一个 trie,并且搜索非常有效。有趣的是,我在 Google 中输入了问题标题,发现 en.wikipedia.org/wiki/String_searching_algorithm 作为第二个搜索结果,点击“使用有限模式集的算法”让我找到了 Aho-Corasick。学习如何使用搜索引擎会很有帮助。
  • 文本的预期长度是多少?字典元素本身有多长?

标签: string algorithm search string-matching


【解决方案1】:

您最好使用Trie。 Trie 更适合查找可能是您正在查找的部分匹配项(即,当您搜索段落的文本时),而不是对字典进行大量调用,而这通常会失败。

我认为 Trie(或某些变体)是合适的原因是因为它的构建正是为了完成您想要做的事情:

如果您使用它(或在每个节点处使用标记词而不是字母的一些修改),这将是存储和检索方面最有效的(至少我所知道的);存储,因为不是在标题中包含该单词的每个 Dict 条目中存储数千次单词“The”(就像电影标题的情况一样),它会在根目录下的一个节点中存储一次。下一个单词“Shawshank”会在一个子节点中,然后“redemption”会在下一个,总共查找3次;然后你会移动到下一个短语。如果它失败了,即短语只是“The Shawshank Looper”,那么在同样的 3 次查找之后你会失败,然后你会转到失败的词 Looper(碰巧它也是根下的一个子节点,并且成功了。假设您正在阅读没有混搭电影名称的段落,此解决方案有效)。

使用哈希表,您将不得不拆分所有单词,检查第一个单词,然后在没有匹配的情况下,继续添加单词并检查该短语是否在字典中,直到获得成功,或者您到达段落的末尾。因此,如果您点击一个没有电影标题的段落,您将获得与该段落中的单词一样多的查找。

【讨论】:

  • 我的字典很大。试一试是不可能的。
  • 如果作者决定使用 Trie,那么将其优化为 Suffix Tree 可能是一个更好的主意?
  • @paypalcomp:不可能? trie 最终会导致更少的存储空间,并且在初始化字典时它们很容易构建。 BrDaHa 并不是建议您拥有一个单词词典和一个 trie,而只是一个 trie,这非常适合您的需要。后缀树是 trie 的一个版本,这也是一个好主意。
  • 我同意,有可以在 O(n + mk) 中使用的传统空间高效算法(例如 Suffix Arrays、Suffix Automata),即在 O(n) 和 k 模式中完成构造在 O(m) 中查找。显然,可以使用一些启发式方法来加快速度。但是,在我看来,最重要的优化是过滤字典本身,即只考虑前 1000 个元素而不是全部 1,000,000。
  • @ile: 正确完成,一旦构建了 trie,搜索速度就不会受到搜索字符串数量的影响——只有被搜索文本的长度和找到的匹配项的数量。所以过滤字典是无关紧要的。
【解决方案2】:

在 Python 中:

import re

movies={1:'The Shawshank Redemption', 2:'The Godfather', 3:'Pretty Woman', 4:'Pulp Fiction'}

text = 'The Shawshank Redemption considered the greatest movie ever made according to the IMDB Top 250.For at least the year or two that I have occasionally been checking in on the IMDB Top 250 The Shawshank Redemption has been battling The Godfather for the top spot.'

repl_str ='(?P<title>' + '|'.join(['(?:%s)' %movie for movie in movies.values()]) + ')'

result = re.sub(repl_str, '<b>\g<title></b>',text)

基本上,它包括从您的 dict 值中形成一个大的替换指令字符串。 我不知道 regex 和 sub 是否对您给它们的替换指令的大小有限制。你可能想检查一下。

【讨论】:

  • 刚刚看到你的字典很大。您可以使用上述方法在 20 部电影的主干中循环播放它。
  • 另一条评论:在执行此操作之前,您应该按字母降序对 dict 进行排序。否则你将无法正确捕捉“教父II”。
  • 但是给定一个来自 para 的字符串,例如:“The Shawshank Redemption Considered the..”你将不得不调用整个 para (蛮力)。想知道是否有更好的出路。
  • 示例:如果您的参数是“a,b,c,d”:您将进行字典调用的组合将是 :a,b,c,d,ab,bc,cd, abc,bcd,abcd.
  • 例如从整个字典构造的 Trie(在另一个解决方案中提到)将小于此解决方案中的指令。
【解决方案3】:

这不是一个完整的答案,更像是一个扩展评论。

在文献中它被称为“多模式匹配问题”。由于您提到模式集包含数百万个元素,因此基于 Trie 的解决方案很可能表现不佳。

据我所知,在实践中,传统的字符串搜索与很多启发式方法一起使用。 DNA 搜索、防病毒检测等所有这些领域都需要快速可靠的模式匹配,因此应该进行大量研究。

我可以想象如何使用带有滚动哈希函数和一些过滤器(布隆过滤器)的 Rabin-Karp 来加快处理速度。例如,您可以先过滤(例如使用弱散列)然后实际验证,而不是实际匹配子字符串,从而减少所需的验证次数。另外,这应该会减少使用原始字典本身完成的工作,因为您将存储它的哈希值或其他过滤器。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-07-19
    • 2021-11-06
    • 2012-07-24
    • 2010-09-08
    • 2013-07-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多