可能重复:
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)
,{{1 }和(3,1)
。
当逐字逐句地发送并且我们假设我们成功找到第一个(3,2)
字母时,程序应该尝试在其旁边的位置找到下一个字母(l
)。所以它应该与e,e,g,k,o,l,m和i匹配,意味着它周围的所有字母:水平,垂直和对角线。在单词中找不到相同的位置两次,因此l
,(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)
无法匹配。
我想知道实现此类功能的最佳方法是什么。如果我在ArrayList中有4x4 (x1, y1) (x2, y2) (x1, y1)
数组和10万个单词,那么最有效和最简单的方法是什么?
答案 0 :(得分:2)
我认为您可能会花费大部分时间来尝试匹配网格无法构建的单词。所以,我要做的第一件事是尝试加快这一步,这应该可以让你在那里大部分时间。
我会将网格重新表达为您按字母索引的可能移动的表格。首先为每个字母分配一个数字(通常是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
然后你创建一个2D布尔数组,告诉你是否有一个可用的字母转换:
| 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
如果他们都被允许,那么可能会找到这个词。
例如,可以在第4次转换(m到e:helMEt)中排除单词“helmet”,因为表中的该条目为false。
可以排除hamster这个词,因为不允许第一个(h到a)转换(甚至不存在于你的表中)。
现在,对于你没有消除的其余单词,尝试在网格中实际找到它们的方式,就像你现在这样做或者在这里的其他一些答案中所建议的那样。这是为了避免因网格中相同字母之间的跳转而导致误报。例如,表格允许使用“帮助”一词,但不允许使用“帮助”一词
让我知道您的boggle手机应用程序何时完成! ;)
答案 1 :(得分:0)
一种研究方法是从网格中生成所有可能的字母(字符串)序列,然后检查这组字符串中是否存在每个单词,而不是根据网格检查每个单词。例如。从第一个网格中的h
开始:
h
hb
he
he // duplicate, but different path
hbc
hbg
hbe
hbe // ditto
heb
hec
heg
...
由于生成序列的开销,这对于非常大的单词列表来说可能更快。对于小的单词列表,可以更快地针对网格单独测试它们。
您可能需要存储整个路径(包括坐标),或者有一个单独的步骤来计算匹配单词的路径。哪个更快将取决于命中率(即您在网格中实际找到的输入字的比例)。
根据您需要实现的目标,您可以将序列与字典单词列表进行比较,以便在开始匹配之前消除非单词。
linked question中有更新2 ,有几种工作的快速解决方案可以从网格生成序列,递归加深以生成更长的序列。然而,他们针对从单词列表生成的Trie测试这些,这使得他们能够尽早放弃序列的子树 - 这会修剪搜索并大大提高效率。这与Markus建议的过渡过滤具有类似的效果。
答案 2 :(得分:0)
虽然我确信在学术上对这个问题有一个美好而有效的答案,但你可以使用相同的方法,但有一个列表可能性。因此,对于“你好”这个词,当你找到字母'h'时,接下来你将添加可能的'e'字母,依此类推。每种可能性都会形成一个字母路径。
答案 3 :(得分:0)
我首先将您的网格视为一个图形,其中每个网格位置都是一个节点,每个节点都连接到它的八个邻居(但是,您不需要在代码中将其显式编码为图形)。一旦找到潜在的起始字母,您需要做的就是从每个起始位置进行深度优先搜索。关键是要记住你已经搜索过的地方,这样你就不会为自己做更多的工作(或者更糟糕的是,陷入困境)。
根据所使用的字符空间的大小,您也可以从构建查找表中受益。我们假设英语(26个连续的字符代码点);如果您从构建一个26个元素的List<Point>[]
数组开始,您可以从网格中填充一次该数组,然后可以快速获取一个位置列表以开始搜索任何单词。例如,要获取h
的位置,我会写arr['h'-'a']
如果您应用相同的策略并为图中的每个边列表构建查找表,您甚至可以进一步利用这一点。您不必搜索每个节点的所有8条边,而是已知道要搜索的边(如果有)。
(注意 - 如果您的角色空间不连续,您仍然可以执行查找表,但您需要使用HashMap<Character,List<Point>>
和map.get('h')
代替。)