「这是我参与2022首次更文挑战的第7天,活动详情查看:2022首次更文挑战」。
题目
给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
示例1
输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
输出:true
示例2
输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "SEE"
输出:true
示例3
输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCB"
输出:false
思路
分析题意,可以得出一些简单的理论,board
中的任何一个位置,都可能作为 word
的起点,向 上、下、左、右
四个方向前进,而每次前进的方向,不能在 走过的路径
上。
如果假设,从坐标 [x, y]
开始,能够搜索到单词 word
,那么必然有这样一个结论:
`[x, y]` 作为起点,`board[x][y]` 必然与 `word` 的首字符 `word.charAt(0)` 相等
同时,`[x, y]` 的 `上、下、左、右` 四个方向,必然也存在某个位置(假设为 [dx, dy]),`board[dx][dy]` 与 `word.charAt(1)` 相等
...
依次类推,可以得出一个校验函数 check(x, y, idx)
,表示从 [x, y]
开始,能否搜索到 word[idx, idx + 1, ...]
利用这个函数,我们以 board
的每个位置作为起点,分别校验 check(x, y, 0)
,如果存在一个最终结果为 true
,则说明搜索成功;如果所有的校验都完成后,依然没有搜索到完整的 word
,则说明 board
不存在 word
;
DFS完整代码
/**
* @param {character[][]} board
* @param {string} word
* @return {boolean}
*/
var exist = function (board, word) {
let m = board.length, n = board[0].length;
// 定义一个访问数组,规格 和 board 一样
const visited = new Array(m).fill(0).map(item => new Array(n).fill(false));
// 四个方向
const directions = [[0, -1], [0, 1], [1, 0], [-1, 0]]
// 检查 从 board[x][y] 开始,能否搜索到 word[k, k + 1, ...] 之后的子串
const check = (x, y, idx) => {
// 如果当前位置不匹配,说明搜索失败
if(board[x][y] != word.charAt(idx)) {
return false;
} else if(idx == word.length - 1) {
// 如果当前配置匹配成功,且匹配进行到了最后一个位置,说明搜索成功
// board[x][y] == word.charAt(word.length - 1)
return true;
}
// 在本次循环中,将当前位置标记为 已访问
visited[x][y] = true;
let result = false;
for(let [dx, dy] of directions) {
// 获取新的位置
let newi = x + dx, newy = y + dy;
// 新位置不能越界
if(newi >= 0 && newi < m && newy >= 0 && newy < n) {
// 不能走已访问的位置
if(!visited[newi][newy]){
// 递归再次校验下一个字符
let flag = check(newi, newy, idx + 1);
// 这里如果返回为true,认为存在某个方向,可继续进行搜索
if(flag) {
result = true;
break;
}
}
}
}
// 需要在使用完毕后,将当前 x,y 的已访问标记删除
visited[x][y] = false;
return result;
}
for(let i = 0; i < m; i++) {
for(let j = 0; j < n; j++) {
// 从起点开始,搜索 word
let flag = check(i, j, 0);
// 这里如果为 true, 说明从0开始,可以一直搜索到 word 的尾部
// 搜索成功
if(flag) {
return true;
}
}
}
// 最后没有搜索成功的话,默认搜索失败
return false;
};
小结
DFS 回溯算法,往往时间复杂度会很高,要注意不必要的递归,进行剪枝
# LeetCode 👉 HOT 100 👉 单词搜索 - 中等题 ✅
合集:LeetCode 👉 HOT 100,有空就会更新,大家多多支持,点个赞👍
如果大家有好的解法,或者发现本文理解不对的地方,欢迎留言评论 😄