题目
给定一个 m x n
二维字符网格 board
和一个字符串单词 word
。如果 word
存在于网格中,返回 true
;否则,返回 false
。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
链接
作为一个初学者,我希望展示一下我的思考过程,以一个没见过这个题目的视角来思考这个问题
思路:
首先就是人工想一下怎么做, 遍历矩阵的每一个元素,作为起始点,判断这个点的四周有没有一个合法的方向, "ABCCD" 以A为起始点,然后看四个方向有没有B
如何转成代码,这个人工模拟的过程,其实就是一个dfs, 深度优先遍历,那怎么设计这个dfs呢,首先肯定是要一个x, y 作为一个当前节点, 然后得到这个
dfs(x, y) -> dfs(x + 1, y) dfs(x - 1, y) dfs(x, y + 1) dfs(x, y - 1)
然后就是如何有一个边界条件呢, 如果我们找到了这个单词,就是path 等于这个单词
len(path) == len(word) 或者如果四个方向都没有下一个字母,我们就return
这样一个大概框架就知道了(只是大概框架,后面很多要修改)
def dfs(x, y):
if word[x][y] == word[len(path)]:
path.append(word[x][y])
else:
return False
if len(path) == len(word)
return True
flag = False
return False
for offset in offsets:
i = x + offset[0]
j = y + offset[1]
if i < 0 of i >= m or j < 0 or j >= n:
continue
flag = flag or dfs(i, j)
path.pop()
return Flag
后面补充细节
1、 首先我们不能走回头路,因为同一个单元格我们无法重复使用
策略: 添加visited
2、 如果flag == False 代表我们当前x,y 是行不通的
策略: 我们以尝试过四个方向全是 False 后, 我们pop出去(恢复现场)
offsets = [[-1, 0], [1, 0], [0, 1], [0, -1]]
class Solution:
def exist(self, board: List[List[str]], word: str) -> bool:
path = []
n = len(board)
m = len(board[0])
def dfs(x, y, visited ):
if board[x][y] == word[len(path)]:
path.append(word[len(path)])
visited[x][y] = True
else:
return False
## 边界条件
if len(path) == len(word):
return True
flag = False
for offset in offsets:
i = x + offset[0]
j = y + offset[1]
if i < 0 or i >= n or j < 0 or j >= m or visited[i][j]:
continue
flag = flag or dfs(i, j,visited)
if flag == False:
visited[x][y] = False
path.pop()
return flag
for i in range(n):
for j in range(m):
path = []
visited = [[False] * m for _ in range(n)]
if dfs(i, j,visited): return True
return False
这样就可以通过了
优化(参考灵神代码)
1、 无需path 直接 用k 代表我们匹配到那个就ok
offsets = [[-1, 0], [1, 0], [0, 1], [0, -1]]
class Solution:
def exist(self, board: List[List[str]], word: str) -> bool:
n = len(board)
m = len(board[0])
def dfs(x, y, visited, k ):
if board[x][y] == word[k]:
visited[x][y] = True
else:
return False
if k == len(word) - 1:
return True
flag = False
for offset in offsets:
i = x + offset[0]
j = y + offset[1]
if i < 0 or i >= n or j < 0 or j >= m or visited[i][j]:
continue
flag = flag or dfs(i, j,visited, k + 1)
visited[x][y] = False
return flag
for i in range(n):
for j in range(m):
path = []
visited = [[False] * m for _ in range(n)]
if dfs(i, j,visited, 0): return True
return False
2、 visited 可以优化
我们可以使用 board 代替visited 数组,如果访问过就把board[x][y]放置为 ' ’ 如果这个点走不通我们再把他恢复(因为肯定是匹配成功的所以恢复为word[k] 就可以, 这个样的好处一个是省空间
一个是我们不用再判断visited 是否为False ,因为匹配不成功在第一个就返回False了
offsets = [[-1, 0], [1, 0], [0, 1], [0, -1]]
class Solution:
def exist(self, board: List[List[str]], word: str) -> bool:
n = len(board)
m = len(board[0])
def dfs(x, y, k ):
if board[x][y] == word[k]:
board[x][y] = ''
else:
return False
if k == len(word) - 1:
return True
flag = False
for offset in offsets:
i = x + offset[0]
j = y + offset[1]
if i < 0 or i >= n or j < 0 or j >= m:
continue
flag = flag or dfs(i, j, k + 1)
board[x][y] = word[k]
return flag
for i in range(n):
for j in range(m):
path = []
if dfs(i, j, 0): return True
return False
3、 优化
我们无需等四个方向都判断完,如果有一个方向返回True 我们可以提前返回
offsets = [[-1, 0], [1, 0], [0, 1], [0, -1]]
class Solution:
def exist(self, board: List[List[str]], word: str) -> bool:
n = len(board)
m = len(board[0])
def dfs(x, y, k ):
if board[x][y] == word[k]:
board[x][y] = ''
else:
return False
if k == len(word) - 1:
return True
flag = False
for offset in offsets:
i = x + offset[0]
j = y + offset[1]
if 0 <= i < n and 0 <= j < m and dfs(i, j, k + 1):
return True
board[x][y] = word[k]
return False
for i in range(n):
for j in range(m):
if dfs(i, j, 0): return True
return False
4、 代码稍微简化一下
offsets = [[-1, 0], [1, 0], [0, 1], [0, -1]]
class Solution:
def exist(self, board: List[List[str]], word: str) -> bool:
n, m = len(board), len(board[0])
def dfs(x, y, k ):
if board[x][y] != word[k]:
return False
if k == len(word) - 1:
return True
board[x][y] = ''
for offset in offsets:
i, j = x + offset[0], y + offset[1]
if 0 <= i < n and 0 <= j < m and dfs(i, j, k + 1):
return True
board[x][y] = word[k]
return False
return any(dfs(i, j ,0) for i in range(n) for j in range(m))
5、 优化1 提前检索一下board 里有没有Word的字符
优化2 假设word[0] 在board 里面有很多, word[-1] 很少,那么我们逆序word 更容易找到答案啊,逆序是不影响最后结果的
offsets = [[-1, 0], [1, 0], [0, 1], [0, -1]]
class Solution:
def exist(self, board: List[List[str]], word: str) -> bool:
cnt = Counter(word)
if not cnt >= Counter(word):
return False
if cnt[word[-1]] < cnt[word[0]]:
word = word[::-1]
n, m = len(board), len(board[0])
def dfs(x, y, k ):
if board[x][y] != word[k]:
return False
if k == len(word) - 1:
return True
board[x][y] = ''
for offset in offsets:
i, j = x + offset[0], y + offset[1]
if 0 <= i < n and 0 <= j < m and dfs(i, j, k + 1):
return True
board[x][y] = word[k]
return False
return any(dfs(i, j ,0) for i in range(n) for j in range(m))