【问题标题】:Searching strings with . wildcard使用 搜索字符串。通配符
【发布时间】:2011-02-02 00:33:57
【问题描述】:

我有一个包含这么多字符串的数组,想在上面搜索一个模式。 这种模式可以有一些“。”匹配(每个)1 个字符(任意)的通配符。

例如:

myset = {"bar", "foo", "cya", "test"}

find(myset, "f.o") -> returns true (matches with "foo") 
find(myset, "foo.") -> returns false 
find(myset, ".e.t") -> returns true (matches with "test")
find(myset, "cya") -> returns true (matches with "cya")

我试图找到一种快速实现该算法的方法,因为myset 实际上是一个非常大的数组,但我的想法没有一个具有令人满意的复杂性(例如O(size_of(myset) * lenght(pattern))

编辑:

myset 是一个巨大的数组,里面的单词并不大。 我可以做一个缓慢的预处理。但是我会有很多find() 查询,所以find() 我希望find() 尽可能快。

【问题讨论】:

  • 每个模式一个或多个通配符?
  • 套装固定了吗?您可以从中构建一个 trie 并将模式与 trie 匹配。
  • 什么语言?您可以使用现有的正则表达式库吗?
  • @Justin:问题不在于正则表达式。问题是将正则表达式应用于集合的所有单词。问题不是匹配单词而是减少匹配的可能单词集合。
  • @Murilo:您不必为所有单词都这样做。我猜较长的单词比较少见,所以你可以选择 8 个字母或更少的单词,然后简单地搜索其余的单词(你必须根据你的字典来决定)。可能有很多重复(取决于字典),这可能会节省大量内存。例如 .e.t 将来自 best、test、rest、lest 等。因此,即使所有这些词都生成 2^4 模式,但总数很少。您甚至可以尝试为前缀生成模式并在“尾部”附近切换到回溯。权衡取舍取决于您拥有的数据。

标签: algorithm string search complexity-theory big-o


【解决方案1】:

您可以为您的集合中所有可能的单词构建语料库的后缀树 (see this link) 使用这种数据结构,您的复杂性将包括构建树的 O(n) 的一次性成本,其中 n 是所有单词长度的总和。

一旦构建树,查找字符串是否匹配只需 O(n),其中 n 是字符串的长度。

【讨论】:

  • 我不知道我是否完全理解,但我认为该树中的 find(myset, "...C") (带有“ABAB”和“BABA”)将导致 O (N*M) 搜索,因为我必须在两个根的子树处分支搜索该模式。
  • 后缀数组在空间上会更好:en.wikipedia.org/wiki/Suffix_array
  • 请详细说明。这将如何工作? -1 到那时,以对抗 +1。
  • 我误读了关于通配符的部分。这将使这不是 O(n) 但我也不认为它会是 O(N*M)。如果没有通配符,它​​将是 O(n),但对于每个通配符,您基本上可以在通配符级别对节点进行广度优先遍历。
  • 对于我在上面的评论中给出的示例,搜索必须访问所有树的节点。所以最坏的情况是O(N*M)
【解决方案2】:

如果集合是固定的,您可以预先计算字符 c 在位置 p 的频率(对于您认为值得的尽可能多的 p 值),然后在数组中搜索一次,对于每个元素在特定位置测试字符的顺序,以便您最有可能提前退出。

【讨论】:

  • 你能给我更多的细节吗?我不明白搜索情况如何。
  • 最坏情况的搜索仍然是 O(n*m)。这只是重新排列字符比较,以更频繁地返回 false 而不是 true,这意味着平均每个单词搜索的字符更少。
  • +1:这是一个合理的启发式方法。基本上,如果您创建一个 trie 并且由于通配符而在 trie 中回溯,则可以根据字符频率选择要采用的下一个分支。这可以概括为一次考虑两个/三个等字符,并且可能会提供更好的性能。见:N-gram search
【解决方案3】:

首先,将语料库按字长划分为集合。然后您的查找算法可以搜索适当的集合,因为find() 的输入始终要求匹配具有特定长度,并且该算法可以设计为适用于所有相同长度的单词。

接下来(对于每个集合),创建一个从字符 x 位置的哈希到匹配单词列表的哈希映射。有大量的哈希冲突是可以的。您可以使用 delta 和 run-length 编码来减少匹配单词列表的大小。

要进行搜索,请为查找输入长度选择适当的哈希映射,并为每个非 . 字符计算该字符 x 位置的哈希,并将 AND 与单词列表一起计算,以获得大大减少的列表。

蛮力搜索那个小得多的列表。

【讨论】:

  • 好。通过答案+1,但我会等待其他解决方案一段时间。
  • 注意像“...”这样的输入,它应该解析为一个简单的测试,看看是否有任何三个字母的单词。
【解决方案4】:

如果您确定您的集合中的单词长度不大。您可能可以创建一个包含以下内容的表:

具有第一个字符'a'的单词列表,具有第一个字符'b'的单词列表,..

具有第二个字符'a'的单词列表,具有第二个字符'b'的单词列表,..

等等。

当您搜索单词时。您可以查找第一个字符与搜索字符串的第一个字符相同的单词列表。使用此细化列表,查找第二个字符与搜索字符串的第二个字符相同的单词,依此类推。您可以忽略“。”每当你遇到他们。

我了解构建表格可能会占用大量空间,但所花费的时间会显着减少。

例如,如果您有 myset = {"bar", "foo", "cya", "test"} 并且您正在搜索 'f.o'

在您检查以 f 开头的单词列表的那一刻,您消除了集合的其余部分。只是一个想法..希望它有所帮助。

【讨论】:

    【解决方案5】:

    我也有同样的问题,我对在互联网上找到的大多数想法/解决方案并不完全满意。我认为这样做的“正确”方法是使用Directed Acyclic Word Graph。我并没有完全这样做,但我在Trie 中添加了一些额外的逻辑以获得类似的效果。

    查看我的isWord() 实现,类似于您想要的find() 接口。它的工作原理是递归 Trie,在通配符上分支,然后将结果收集回一个公共集合中。 (见findNodes()。)

    getMatchingWords() 在本质上是相似的,只是它返回匹配单词的集合,而不仅仅是一个关于查询是否匹配任何内容的布尔值。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-19
      • 1970-01-01
      • 2017-07-17
      • 2018-08-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多