#前缀树–单词搜索II
问题描述
给定一个二维网格 board 和一个字典中的单词列表 words,找出所有同时在二维网格和字典中出现的单词。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母在一个单词中不允许被重复使用。
示例:
输入:
words = [“oath”,“pea”,“eat”,“rain”] and board =
[
[‘o’,‘a’,‘a’,‘n’],
[‘e’,‘t’,‘a’,‘e’],
[‘i’,‘h’,‘k’,‘r’],
[‘i’,‘f’,‘l’,‘v’]
]
输出: [“eat”,“oath”]
说明:
你可以假设所有输入都由小写字母 a-z 组成。
提示:
1、你需要优化回溯算法以通过更大数据量的测试。你能否早点停止回溯?
2、如果当前单词不存在于所有单词的前缀中,则可以立即停止回溯。什么样的数据结构可以有效地执行这样的操作?散列表是否可行?为什么? 前缀树如何?
问题分析
我一开始没有理解题意,我以为题目中“同一个单元格内的字母在一个单词中不能重复出现”的意思是:同一个字母不能出现在两个单词中。后来看了参考答案,我才发现是我想多了。题目只是要求同一个字母不能重复出现在一个单词中,但可以出现在两个单词中。同时,根据参考答案,我才理解最终输出的匹配单词中不能有重复的单词。根据这几个原则,我们就可以实现我们的代码。
首先,我们采用前缀树存储要匹配的单词,可以节省空间和搜索时间。然后以每一个字母为中心,向上下左右分别搜索,如果匹配到了单词,就返回并存储下来;如果不匹配就不停止搜索。同时每当匹配到一个单词或节点没有子节点时,就对前缀树实现剪枝,从而极大优化算法复杂度。
具体代码
`代码一共分为两部分,第一部分construct()是构造前缀树的函数,后一部分findWords()就是对每一个字母进行搜索来匹配单词。
class Solution:
def __init__(self):
self.lookup = {}
self.word_matched = []
def construct(self, words):
for word in words:
tree = self.lookup
for a in word:
if a not in tree:
tree[a] = {}
tree = tree[a]
tree['#'] = word
def findWords(self, board: List[List[str]], words: List[str]) -> List[str]:
self.construct(words)
#以board中的每一个字母为中心,进行上下左右遍历,如果不存在单词或找到单词就停止回溯
for i in range(len(board)):
for j in range(len(board[0])):
if board[i][j] in self.lookup:
self.recall(board, i, j, self.lookup)
return self.word_matched
def recall(self, board, i, j, parent):
letter = board[i][j]
tree = parent[letter]
if '#' in tree:
self.word_matched.append(tree.pop('#', False))
#将每一个被遍历过的节点都标记一下,防止重复遍历
board[i][j] = '$'
#对board[i][j]的上下左右进行遍历
for (rowOffset, colOffset) in [(-1, 0), (0, 1), (1, 0), (0, -1)]:
newi, newj = i + rowOffset, j + colOffset
if newi <0 or newi >= len(board) or newj < 0 or newj >= len(board[0]):
continue
if board[newi][newj] not in tree:
continue
self.recall(board, newi, newj, tree)
if not tree:
parent.pop(letter)
board[i][j] = letter
return