剑指 12 矩阵中的路径
原题目
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如,在下面的3×4的矩阵中包含一条字符串“bfce”的路径(路径中的字母用加粗标出)。
[[“a”,“b”,“c”,“e”],
[“s”,“f”,“c”,“s”],
[“a”,“d”,“e”,“e”]]
但矩阵中不包含字符串“abfb”的路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入这个格子。
考查知识点
回溯法:
可以认为是暴力法的升级版,在一个状态s1_1
同样考虑其所有的动作{a1,a2,a3,...}
,如果执行完所有的动作都没有满足题目,就回溯到前一个状态s0
,站在s0
的角度继续看与s1_1
相同地位的s1_2
采取所有的动作是否能满足题目要求。
好的解法
bool hasPath(char *matrix, int rows, int cols, char *str)
{
if(matrix==nullptr || rows<1 || cols<1 || str== nullptr)
return false;
bool *visited = new bool[rows*cols];
memset(visited, 0, rows*cols);
int pathLength = 0; // 已经匹配的长度
for(int row=0; row<rows; ++row)
{
for(int col=0; col<cols; ++col)
{
//判断当前遍历的矩阵元素是否与路径中的当前元素相等
if(hasPathCore(matrix, rows, cols, row, col, str, pathLength, visited)) return true;
}
}
delete [] visited;
return false;
}
bool hasPathCore(const char *matrix, int rows, int cols, int row, int col, const char* str, int& pathLength, bool *visited)
{
// 若字符串已经匹配到末尾,则在该矩阵中找到该字符串,返回true
if(str[pathLength] == '\0') return true;
bool hasPath = false;
//若当前遍历到的矩阵元素与路径中的某元素相等,并且该矩阵元素的位置以前没有访问过,接着访问该矩阵元素周围四个方向上的元素
if(row>=0 && row<rows && col>=0 && col<cols && matrix[row*cols + col]==str[pathLength] && !visited[row*cols + col])
{
++pathLength;
visited[row*cols + col] = true;
hasPath = hasPathCore(matrix, rows, cols, row, col-1, str, pathLength, visited)
|| hasPathCore(matrix, rows, cols, row-1, col, str, pathLength, visited)
|| hasPathCore(matrix, rows, cols, row, col+1, str, pathLength, visited)
|| hasPathCore(matrix, rows, cols, row+1, col, str, pathLength, visited);
// 若周围四个方向上的元素都不匹配,回溯上一个已经匹配的矩阵元素
if (!hasPath)
{
--pathLength;
visited[row*cols + col] = false;
}
}
return hasPath;
}
上面的代码是剑指书上的源代码,通过在hasPath()
中调用回溯函数hasPathCore()
,在回溯函数内部使用递归。思想就是判断当前矩阵元素与字符串当前元素是否相等,如果相等就递归调用自身查看当前矩阵元素四周元素与字符串下一元素是否相等,如果四周的四个元素都不满足条件,则回溯到已经匹配的上一个节点。
优化
class Solution {
public:
bool exist(vector<vector<char>>& board, string word) {
if(word.empty() || board.empty() || board[0].empty()) return false;
for (int row=0; row<board.size(); ++row)
{
for (int col=0; col<board[0].size(); ++col)
{
//回溯:字符串word是否在矩阵中
if (backTracking(board, row, col, word, 0)) return true;
}
}
return false;
}
bool backTracking(vector<vector<char>> &board, int row, int col, string &word, int w)
{
//索引越界,或者当前矩阵元素与字符串当前元素值不匹配
if (row<0 || row>=board.size() || col<0 || col>=board[0].size() || board[row][col]!=word[w]) return false;
//进入到这里说明当前矩阵元素与字符串当前元素值匹配
if (w==word.length()-1) return true; //字符串已经匹配完毕
//进入到这里说明当前矩阵元素与字符串当前元素匹配,要查看当前矩阵元素周围四个位置的元素与字符串下一元素是否匹配
char temp = board[row][col];
board[row][col] = '\0';
if (backTracking(board, row+1, col, word, w+1)
|| backTracking(board, row-1, col, word, w+1)
|| backTracking(board, row, col+1, word, w+1)
|| backTracking(board, row, col-1, word, w+1))
{
return true;
}
//进入到这里说明当前矩阵元素上下左右与字符串下一元素都不匹配,将当前元素恢复原本值
board[row][col] = temp;
return false;
}
};
优化代码来自力扣链接字节题库 - #剑12 - 中等 - 矩阵中的路径 - 1刷
主要是对标志是否访问过矩阵某一位置元素的布尔矩阵进行优化,直接在原矩阵上将已经访问过的元素修改为字符串中不可能出现的字符,表示已经访问过得元素不能再参加比较