这是我参与「掘金日新计划 · 8 月更文挑战」的第11天,点击查看活动详情
题目
给定一个二维网格和一个单词,找出该单词是否存在于网格中。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
示例:
给定 word = "ABCCED", 返回 true
输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
输出:true
给定 word = "SEE", 返回 true
输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "SEE"
输出:true
给定 word = "ABCB", 返回 false
输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCB"
输出:false
来源:力扣(LeetCode)
链接:leetcode-cn.com/problems/wo…
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路
遍历矩阵,每个字符 都 可以做为起始字符去搜索。具体搜索方法用DFS即可,从头到尾的去匹配 word 中的字符,矩阵中是按四个方向去尝试,当前字符匹配上了,就看四个方向与下一个字符进行匹配,直到字串匹配完成,或者有不匹配的就终止。
在DFS过程中需要做一下标记,以防止字符被重复使用,比如当前字符匹配了,查看四个方向与下一个字符,但再 下次的匹配时不能重复用这次的字符,DFS是四个方向,它是可能再回来的,所以需要做标记。但这个标记只是防止匹配过程中矩阵中的字符不被重复使用。
步骤:
- 以"SEE"为例,首先要选起点:遍历矩阵,找到起点S。
- 起点可能不止一个,基于其中一个S,看看能否找出剩下的"EE"路径。
- 下一个字符E有四个可选点:当前点的上、下、左、右。
- 逐个尝试每一种选择。基于当前选择,为下一个字符选点,又有四种选择。
- 每到一个点做的事情是一样的。DFS 往下选点,构建路径。
- 当发现某个选择不对,不用继续选下去了,结束当前递归,考察别的选择。
代码
const exist = (board, word) => {
const m = board.length;
const n = board[0].length;
const used = new Array(m); // 二维矩阵used,存放bool值
for (let i = 0; i < m; i++) {
used[i] = new Array(n);
}
// canFind 判断当前点是否是目标路径上的点
const canFind = (row, col, i) => { // row col 当前点的坐标,i当前考察的word字符索引
if (i == word.length) { // 递归的出口 i越界了就返回true
return true;
}
if (row < 0 || row >= m || col < 0 || col >= n) { // 当前点越界 返回false
return false;
}
if (used[row][col] || board[row][col] != word[i]) { // 当前点已经访问过,或,非目标点
return false;
}
// 排除掉所有false的情况,当前点暂时没毛病,可以继续递归考察
used[row][col] = true; // 记录一下当前点被访问了
// canFindRest:基于当前选择的点[row,col],能否找到剩余字符的路径。
const canFindRest = canFind(row + 1, col, i + 1) || canFind(row - 1, col, i + 1) ||
canFind(row, col + 1, i + 1) || canFind(row, col - 1, i + 1);
if (canFindRest) { // 基于当前点[row,col],可以为剩下的字符找到路径
return true;
}
used[row][col] = false; // 不能为剩下字符找到路径,返回false,撤销当前点的访问状态
return false;
};
for (let i = 0; i < m; i++) { // 遍历找起点,作为递归入口
for (let j = 0; j < n; j++) {
if (board[i][j] == word[0] && canFind(i, j, 0)) { // 找到起点且递归结果为真,找到目标路径
return true;
}
}
}
return false; // 怎么样都没有返回true,则返回false
};