题目描述:给定一个 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","d"]], word = "abcd"
输出:false
解题思路
DFS + 剪枝
DFS:深度搜索二叉树
剪枝:遇到不符合条件的情况,提前返回,即答案中代码的
// 如果越界(行或列) 或 匹配到的字符与 word 当前遍历到的下标不同,直接返回
if(i<0||i>=row||j<0||j>=col||board[i][j]!=word[k]) return false;
DFS 解析:
递归参数: 当前元素在矩阵 board 中的行列索引 i 和 j,当前目标字符在 word 中的索引 k 。
终止条件:
返回 false:
行或列索引越界
当前矩阵元素与目标字符不同
当前矩阵元素已访问过(通过将访问过的字符置空)
返回 true: k = word.length() - 1 ,即字符串 word 已全部匹配。
递推过程:
标记当前矩阵元素: 将 board[i][j] 修改为空字符'\0',代表此元素已访问过,防止之后搜索时重复访问。
搜索下一单元格: 朝当前元素的 上、下、左、右 四个方向开启下层递归,使用或连接(代表只需找到一条可行路径就直接返回,不再做后续 DFS ),并记录结果至 res 。
还原当前矩阵元素: 将 board[i][j] 元素还原至初始值,即 word[k] 。
返回值: 返回布尔量 res ,代表是否搜索到目标字符串。
class Solution {
public:
bool exist(vector<vector<char>>& board, string word)
{
len=word.size();
row=board.size();
col=board[0].size();
for(int i=0;i<row;i++)//从矩阵的任意一个位置开始搜索
{
for(int j=0;j<col;j++)
{
if(dfs(board,word,i,j,0)) return true;
}
}
return false;
}
int row,len,col;
bool dfs(vector<vector<char>>& board, string word,int i,int j,int k)
{
if(i<0||i>=row||j<0||j>=col||board[i][j]!=word[k]) return false;
if(k==len-1) return true;
board[i][j]='\0';
bool res=dfs(board,word,i-1,j,k+1)||dfs(board,word,i+1,j,k+1)
||dfs(board,word,i,j-1,k+1)||dfs(board,word,i,j+1,k+1);
board[i][j]=word[k];//搜索完成后,不管return 什么,board必须还原,因为主函数里面还要
//换一个位置为起点再次进行搜索。如果不还原,则board数组里面有些位置被更改,影响主函数里
从下一个位置开始搜索的结果,board不还原对于单次搜索来说是没有影响的,但是我们要从board
数组里面的任意位置为起点进行多次搜索,所以每一次搜索完成后,在回溯的过程中board数组必
须还原。
return res;
}
};
- dfs + 回溯;
- 使用二维布尔数组记录之前的位置是否已经被访问过,如果已经被访问过的话,则在 dfs 的过程中,直接 return false 即可。也就是说,此路已经不通了;
- 如果当前遍历到的字符不等于 board[i][j] 位置上的字符,那么说明此路也是不通的,因此返回 false;
- 至于递归结束的条件:如果指针 k 能够来到 word 的最后一个字符,那么说明能够在矩阵 board 中找到一条路径,此时返回 true;
- 在遍历到当前 board[i][j] 的时候,首先应将该位置的 visited[i][j] 设置为 true,表明访问过;
- 然后,递归地去 board[i][j] 的上下左右四个方向去找,剩下的路径;
- 在 dfs 的过程当中,如果某条路已经不通了,那么那么需要回溯,也就是将 visited[i][j] 位置的布尔值重新赋值为 fasle;
- 最后,返回 ans 即可。
class Solution {
public:
bool exist(vector<vector<char>>& board, string word)
{
row = board.size();
col = board[0].size();
len = word.size();
vector<vector<bool>> visited(row, vector<bool>(col, false));
for(int i=0; i<row; i++)
{
for(int j=0; j<col; j++)
{
// 从 (0, 0) 点开始进行 dfs 操作,不断地去找,
// 如果以(0,0) 点没有对应的路径的话,那么就从 (0, 1) 点开始去找
if(dfs(board, word, visited, i, j, 0)) return true;
}
}
// 遍历结束没找到false
return false;
}
int row, col, len;
bool dfs(vector<vector<char>>& board, string& word, vector<vector<bool>>& visited, int i, int j, int k)
{
// 判断传入参数的可行性 i 与图行数row比较,j与图列数col比较
// i,j初始都是0,都在图左上角
// k是传入字符串当前索引,一开始是0,如果当前字符串索引和图当前索引对应的值不相等,表示第一个数就不相等
// 所以继续找第一个相等的数。题目说第一个数位置不固定,即路径起点不固定(不一定是左上角为第一个数)
// 如果board[i][j] == word[k],则表明当前找到了对应的数,就继续执行(标记找过,继续dfs 下上右左)
if(i<0 || i>=row || j<0 || j>=col || visited[i][j] == true || board[i][j] != word[k])
{
return false;
}
// 表示找完了,每个字符都找到了
if(k == len-1) return true;
//标记当前网格上的点已经访问
visited[i][j] = true;
bool ans = false;
// 顺序是 上 下 左 右;上面找到了对应索引的值所以寻找字符串的下一个字符k+1
ans = dfs(board, word, visited, i-1, j, k+1) || dfs(board, word, visited, i+1, j, k+1)
|| dfs(board, word, visited, i, j-1, k+1) || dfs(board, word, visited, i, j+1, k+1);
// 还原找过的元素,因为之后可能还会访问到(不同路径)
visited[i][j] = false;
// 返回结果,如果false,则if(dfs(board, words, i, j, 0)) return true;不会执行,就会继续找
return ans;
}
};