【问题标题】:Searching words in array [duplicate]在数组中搜索单词[重复]
【发布时间】:2012-11-10 21:54:34
【问题描述】:

可能重复:
How to find list of possible words from a letter matrix [Boggle Solver]

我有一个String[][] 数组,例如

h,b,c,d
e,e,g,h
i,l,k,l
m,l,o,p

我需要将 ArrayList 与此数组进行匹配,以找到 ArrayList 中指定的单词。在搜索单词hello 时,我需要得到一个正匹配和字母的位置,例如在这种情况下(0,0)(1,1)(2,1)(3,1)(3,2)

当逐个字母地查找时,假设我们成功定位了第一个l 字母,程序应该尝试在它旁边的位置找到下一个字母(l)。所以它应该与 e、e、g、k、o、l、m 和 i 匹配,这意味着它周围的所有字母:水平、垂直和对角线。同一个位置不能在单词中找到两次,所以(0,0)(1,1)(2,1)(2,1)(3,2) 是不可接受的,因为位置(2,1) 匹配了两次。在这种情况下,两者都会匹配单词,因为允许对角位置,但它需要匹配另一个 l,因为要求一个位置不能多次使用。

这种情况也应该匹配

h,b,c,d
e,e,g,h
l,l,k,l
m,o,f,p 

如果我们假设我们尝试搜索helllo,它将不匹配。 (x1, y1) (x1, y1)(x1, y1) (x2, y2) (x1, y1) 无法匹配。

我想知道实现这种功能的最佳方式是什么。如果我在 ArrayList 中有 4x4 String[][] 数组和 100 000 个单词,那么最有效和最简单的方法是什么?

【问题讨论】:

  • 我已经尝试按照我描述的程序应该可以工作:找到第一个字母,匹配它周围的字母并重复该过程。当它周围有多个相同的字母时会出现问题。第二个问题是什么,它可以从任何单元格开始。单词的长度是 3 到 10 个字母(我的语言中最常用的单词)。另外,我想讨论最好的解决方案,而不是如何修复我的缓慢和错误的版本。我认为最好的解决方案是类似的。
  • 如果我理解正确,您将无法返回,即在 (0,0) 处读取 h 后,您无法在同一搜索周期中再次读取它。在这种特殊情况下(少量字母,大量单词),我将创建所有可能组合的列表(不仅保存字母,还保存整个路径),例如使用深度优先搜索(因为它不需要大量内存以节省您的中间步骤)。但是响应确实很大程度上取决于问题的条件,如果数组变得比 4x4 大得多,这个解决方案就会有严重的问题。
  • 是的,一个坐标只能使用一次,所以(0,0)不能被多次访问。
  • @Serhiy:查看我对 DNA 答案的评论...

标签: java arrays string search boggle


【解决方案1】:

我认为您可能会花费大部分时间来尝试匹配您的网格不可能构建的单词。所以,我要做的第一件事就是尝试加快这一步,这应该可以让你大部分时间到达那里。

我会将网格重新表达为您按字母索引的可能移动的表格。首先为每个字母分配一个数字(通常是 A=0、B=1、C=2、……等等)。对于您的示例,让我们只使用您拥有的字母的字母表(在第二个网格中,最后一行显示为“ m o f p ”):

 b | c | d | e | f | g | h | k | l | m |  o |  p
---+---+---+---+---+---+---+---+---+---+----+----
 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11

然后你创建一个二维布尔数组,告诉你是否有特定的字母转换可用:

     |  0  1  2  3  4  5  6  7  8  9 10 11  <- from letter
     |  b  c  d  e  f  g  h  k  l  m  o  p
-----+--------------------------------------
 0 b |     T     T     T  T     
 1 c |  T     T  T     T  T
 2 d |     T           T  T
 3 e |  T  T     T     T  T  T  T
 4 f |                       T  T     T  T
 5 g |  T  T  T  T        T  T  T
 6 h |  T  T  T  T     T     T  T
 7 k |           T  T  T  T     T     T  T
 8 l |           T  T  T  T  T  T  T  T  T
 9 m |                          T     T
10 o |              T        T  T  T
11 p |              T        T  T
 ^
 to letter

现在浏览您的单词列表并将单词转换为转换(您可以预先计算):

hello (6, 3, 8, 8, 10):
6 -> 3, 3 -> 8, 8 -> 8, 8 -> 10

然后通过在您的表格中查找这些转换来检查它们是否允许:

[6][3] : T
[3][8] : T
[8][8] : T
[8][10] : T

如果都允许的话,就有可能找到这个词。

例如,单词“helmet”可以在第 4 次转换(m 到 e:helMEt)时被排除,因为您表中的该条目是错误的。

并且可以排除仓鼠这个词,因为不允许第一个(h 到 a)转换(甚至在您的表中不存在)。

现在,对于您没有消除的剩余单词,请尝试按照您现在的方式或按照此处其他一些答案中的建议在网格中实际找到它们。这是为了避免由于网格中相同字母之间的跳跃而导致的误报。例如,表格允许使用“帮助”一词,但网格不允许使用“帮助”一词

当你的手机应用程序完成时告诉我! ;)

【讨论】:

    【解决方案2】:

    虽然我确信在学术上对这个问题有一个美丽而有效的答案,但您可以使用相同的方法,但有一个列表可能性。因此,对于单词“hello”,当您找到字母“h”时,接下来您将添加可能的“e”字母,依此类推。每一种可能性都会形成一条字母的路径。

    【讨论】:

      【解决方案3】:

      我首先将您的网格视为一个图形,其中每个网格位置都是一个节点,每个节点都连接到它的八个邻居(但是,您不需要在代码中将其显式编码为图形)。一旦找到潜在的起始字母,您需要做的就是从每个起始位置对图形进行深度优先搜索。关键是要记住你已经搜索过的地方,这样你就不会为自己做更多的工作(或者更糟的是,陷入循环中)。


      根据所使用的字符空间的大小,您可能还可以从构建查找表中受益。让我们假设英语(26 个连续字符代码点);如果您从构建一个 26 元素 List&lt;Point&gt;[] 数组开始,您可以从网格中填充该数组一次,然后可以快速获取位置列表以开始搜索任何单词。例如,要获取h 的位置,我会写arr['h'-'a']

      如果您应用相同的策略并为图中的每个边列表构建查找表,您甚至可以进一步利用这一点。不必为每个节点搜索所有 8 条边,您已经知道要搜索哪些边(如果有的话)。

      (请注意 - 如果您的字符空间不连续,您仍然可以进行查找表,但您需要使用 HashMap&lt;Character,List&lt;Point&gt;&gt;map.get('h')。)

      【讨论】:

        【解决方案4】:

        一种调查方法是从网格中生成所有可能的字母(字符串)序列,然后检查每个单词是否存在于这组字符串中,而不是对照网格检查每个单词。例如。从您的第一个网格中的h 开始:

        h
        hb
        he
        he // duplicate, but different path
        hbc
        hbg
        hbe
        hbe // ditto
        heb
        hec
        heg
        ...
        

        由于生成序列的开销,这对于非常大的单词列表可能会更快。对于小的单词列表,根据网格单独测试它们会更快。

        您要么需要存储整个路径(包括坐标),要么有一个单独的步骤来计算匹配单词的路径。哪个更快取决于命中率(即您在网格中实际找到的输入词的比例)。

        根据您需要实现的目标,您也许可以将序列与字典单词列表进行比较,以在开始匹配之前消除非单词。

        更新 2linked question 中有几个有效的、快速的解决方案,可以从网格生成序列,递归深化以生成更长的序列。然而,他们将这些与从单词列表生成的 Trie 进行测试,这使他们能够尽早放弃序列的子树——这会修剪搜索并大大提高效率。这与 Markus 建议的过渡过滤效果相似。

        【讨论】:

        • 如果网格是 4x4(16 个可能的起点),允许对角线移动(下一个字母平均有 4.25 个选择),最长的单词有 10 个字母,那么可能有大约 700 万种可能的组合(16 x 4.25^9)...所以通过字符串列表并尝试在网格中匹配它们肯定会更快。
        • 在这种情况下,对于 100,000 个输入词,您需要测试多少种组合?晚上这个时候我的数学很弱,但是 100000 * 16 * 4.25^4 是否正确? (5 亿)假设平均字长为 5
        • 肯定少得多,因为您会逐个字母地排除选项,而不是遵循所有可能的路径,而在您的想法中,您确实必须针对 100000 个单词中的每一个单词检查所有 700 万种可能性。
        • 好点,它会少很多,因为您可以在继续进行时消除一些单词。但是不,您不必检查 7M * 100K(不是通过蛮力) - 您可以使用 O(1) HashSet 查找或类似方法来执行此操作。一旦你生成了 7M 的可能性,对照它们检查 100K 单词是非常快的。我已经更新了我的答案以建议一种混合方法。
        猜你喜欢
        • 2015-11-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-11-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多