题目描述
题目链接:https://leetcode-cn.com/problems/ju-zhen-zhong-de-lu-jing-lcof/
解题思路
深度优先搜索,每访问一个数,都可以当作DFS的入口。然后递归再访问它的上下左右四个邻居,根据其是否与字符串中的字符是否匹配决定是否继续搜索。匹配成功就继续搜索邻居,不成功就返回False。当匹配长度等于字符串长度时就说明存在一条通路了,这时候便可返回True。
需要注意,每访问一个结点,要深搜其邻居前要换成其他字符(标记为已经访问)。然后搜索之后再变回原来的字符(撤回标记)。为什么要这么操作呢?可以由下图简单理解。
为什么要添加标记?
退一步走,假设A是B的邻居,那么A递归深搜时可以访问B,如果A没有标记为已经访问,那么B递归深搜时又可以访问A,然后A又递归深搜B。。。以此无穷尽也,这也是为什么标记的作用了。用大白话讲,我是你大哥,你就只能是我小弟。不能你是我大哥,我也是你大哥。
为什么又要撤回标记?
而撤回标记,则代表以A为入口进行深搜时找不到匹配的通路,但是不单表A不能作为其他位置字符入口进行深搜时一个可行的通路呀。如上图所示。用白话讲就是,我当不了大哥,但是我可以当小弟呀。
解答代码如下:
class Solution {
public:
bool exist(vector<vector<char>>& board, string word) {
for(int i=0; i<board.size(); ++i) // 行坐标
for(int j=0; j<board[0].size(); ++j) // 列坐标
// 找到某入口位置,能够通这条路径
{
if (board[i][j] == word[0] && dfs(board, word, 0, i,j))
return true; // 优化,找准入口再深搜 优化不了多少
}
return false;
}
bool dfs(vector<vector<char>>& board, string word, int len, int x, int y)
{
if(x<0 || y<0 || x>=board.size() || y>=board[0].size()|| board[x][y]!=word[len])
// 坐标越界或者字符不匹配
// 先判断坐标越界,因为本来就已经越界了的话,还访问board[x][y]就会报错
return false;
// 能执行下面的语句表明当前字符是匹配的
// 递归先写返回条件
if(len == word.size()-1) // len表示当前匹配长度
return true;
// 字符匹配,先标记当前位置,表示已经走过
// 因为该结点也是它邻居的邻居,深搜的话就死循环了 = =
char temp = board[x][y];
board[x][y] = '*';
// 深搜其四个邻居,上下左右
// 如果坐标越界,刚进入就会返回false,所以这里不需要再次判断越界
int dx[4]={-1,1,0,0}, dy[4]={0,0,-1,1};
for(int i=0;i<4;++i)
{
if(dfs(board, word, len+1, x+dx[i], y+dy[i]))
return true;
}
// 四条路没一个走的通,那么就换一个入口试试,所以返回false之前恢复该入口。
board[x][y] = temp;
return false;
}
};
深搜邻居的时候我也尝试过像其他网友这样
然后return res。 但是好像这样时间和空间都不如for循环依次访问。
下面这位大佬给出了加速小技巧,也可以学习一下