【问题标题】:Matching substrings from a dictionary to other string: suggestions?将字典中的子字符串匹配到其他字符串:建议?
【发布时间】:2010-01-06 15:30:25
【问题描述】:

你好堆栈溢出的人。我想就以下问题提出一些建议。我正在使用 Java。

我有一个带有多个字符串的数组 #1。例如,其中两个字符串可能是:“一个苹果落在牛顿的头上”和“苹果长在树上”。

另一方面,我有另一个数组 #2,其中包含 (Fruits => Apple, Orange, Peach; Items => Pen, Book; ...) 之类的术语。我将这个数组称为我的“字典”。

通过将一个数组中的项目与另一个数组进行比较,我需要查看 #1 中的项目从 #2 属于哪个“类别”。例如。来自 #1 的两者都属于“水果”。

我最重要的考虑是速度。我需要快速完成这些操作。允许恒定时间检索的结构会很好。

我考虑了一个带有 contains() 方法的 Hashset,但它不允许子字符串。我还尝试在不区分大小写标志的情况下运行像 (apple|orange|peach|...etc) 这样的正则表达式,但我读到当术语数量增加时它不会很快(预计至少 200 个)。最后,我搜索并正在考虑使用带有 indexOf() 的 ArrayList,但我不知道它的性能。我还需要知道哪些术语实际匹配,所以在这种情况下,它将是“Apple”。

请提供您对此问题的看法、想法和建议。

我看到了 Aho-Corasick 算法,但关键字/术语很可能经常变化。所以我认为我不能使用它。哦,我不是文本挖掘和数学方面的专家,所以请详细说明复杂的概念。

感谢 Stack Overflow 的人们抽出宝贵时间! :)

【问题讨论】:

  • 我已经检查了后缀树。它似乎类似于 Aho-Corasick 算法使用的 Trie 结构。我担心的是我有许多不同的类别,每个类别有很多术语。为每个类别构建一棵树对我来说似乎效率低下。谢谢马特K!
  • 实际上,我认为您不需要为每个类别构建一棵树。您应该能够将多个字符串插入到单个后缀树中,并在每个有效字符串的树的终止点处添加对类别对象的引用。
  • 这个想法很有趣!但我不明白您答案的“添加对类别对象的引用”部分。我该怎么做?
  • 您将需要一些与树中的每个字符相关联的数据来指示树中的哪些字符串(如果有)以该字符终止。您可以将有关字符串所属的零个或多个类别的数据与其终止数据相关联。

标签: java nlp


【解决方案1】:

如果您使用 Google Collections 中的多地图,它们具有反转地图的功能(因此您可以从 {"Fruits" => [Apple]} 之类的地图开始,然后生成带有 {"Apple" = > ["Fruits"]}。因此,您可以在一次调用地图时查找该单词并找到它的类别列表。

我希望我想自己拆分字符串并一次在地图中查找单词,这样我就可以进行词干提取(针对不同的词尾进行调整)和停用词过滤。使用地图应该可以获得很好的查找时间,而且很容易尝试。

【讨论】:

  • Stemming... 现在这很有趣,但我错过了。如果我可以将“苹果长在树上”的标题(是这样叫的吗?)标题“苹果长在树上”并标记化,我不再需要子字符串匹配。 Hashset 的 contains() 方法可以满足我的需要。谢谢内森·休斯。 :D +1 词干提示!
【解决方案2】:

suffix tree 或类似的数据结构是否适用于您的应用程序?它提供 O(m) 字符串查找,其中 m 是搜索字符串的长度,在 O(n2) 之后 - 或者更好地使用一些技巧 - 初始设置,并且还有一些额外的努力,您可以将任意数据(例如对类别的引用)与字典中的完整单词相关联。如果您不想自己编写代码,我相信BioJava 库包含一个实现。

您也可以在初始设置后将字符串添加到后缀树,尽管成本仍将在 O(n2) 左右。如果您要添加简短的单词,这可能没什么大不了的。

【讨论】:

  • 请注意,后缀树是线性(在空间和时间上)结构。
  • 你说得对——这会教我早上第一件事就是回答问题。当然,搜索在搜索字符串的长度上是线性的,而不是在树中包含的字符串的长度上,这仍然非常有效。无论如何,编辑答案以反映这一点。
  • 您可能想考虑将 Knuth-Morris-Pratt 与 Trie 一起使用,但这可能会或可能不会提高速度(如果有,您可能会也可能不会在意)。
【解决方案3】:

如果您只有 200 个要查找的术语,那么正则表达式实际上可能对您有用。当然,正则表达式很大,但如果你编译一次并只使用这个编译后的 Pattern,查找时间可能与 array#1 中所有字符串的组合长度成线性关系,我不明白你怎么能希望成为比那更好。

所以算法是:将要查找的array#2的单词连接到正则表达式中,编译它,然后在array#1中找到匹配项。

(正则表达式被编译到状态机中——即在字符串的每个字符上,它只是为下一个状态进行表查找。如果正则表达式很复杂,您可能需要回溯,这会增加时间,但您的常规表达式的结构非常简单。)

【讨论】:

  • 我的正则表达式确实很简单。对于所有关键字,只需 (apple|oranges|peach|...etc),每个类别一个正则表达式。不过我怀疑它的性能。我确实编译了模式以供重用。
  • 我完全不明白你想做什么。但是,如果您想在数组#1 中的所有字符串中搜索数组#2 中出现的任何内容,我可能会使用其中出现的所有内容制作一个巨大的正则表达式,然后进行搜索。否则,您的搜索次数与类别一样多。我发现的所有内容都会在 HashMap 中查找,该 HashMap 将单词映射到它们的类别。要查看这是否可行,您可以将尽可能多的随机单词连接到如此庞大的正则表达式中并检查搜索时间。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-14
  • 2021-09-22
  • 2019-05-01
  • 2014-07-12
  • 1970-01-01
  • 2021-08-25
相关资源
最近更新 更多