PROBLEM

剑指 Offer 12. 矩阵中的路径

难度 中等

MY ANSWER

使用DFS与回溯解决,矩阵visit表示搜索中是否走过该格子,index代表当前需要匹配的字符串的字符。dfs(int i, int j, int index)表示从matrix(i, j)出发能否搜索到从索引k开始的word(k, -1)。执行时:

  • 当前字符不匹配,直接返回false;
  • 当前字符匹配且已经是末尾,直接返回true;
  • 当前字符匹配但不是末尾,将当前格子设置为访问过。递归遍历未访问过的相邻格子,若相邻格子中的dfs有返回true,则返回true,否则返回false。这一步返回前要将当前格子设置为未访问,进行回溯。

时间复杂度O(MN3^K),K为字符串长度,但由于剪枝,时间复杂度并不会这么大。空间复杂度O(MN),开销用于visit矩阵。

class Solution {
public:
vector<vector<char>> matrix;
vector<vector<int>> visit;
string str;
bool dfs(int i, int j, int index) {
if(matrix[i][j] != str[index]) {
return false;
}
else if(index == str.size() - 1) {
return true;
}
visit[i][j] = 1;
if(i + 1 < matrix.size() && !visit[i + 1][j]) {
if(dfs(i + 1, j, index + 1)) {
visit[i][j] = 0;
return true;
}
}
if(j + 1 < matrix[0].size() && !visit[i][j + 1]) {
if(dfs(i, j + 1, index + 1)) {
visit[i][j] = 0;
return true;
}
}
if(i - 1 >= 0 && !visit[i - 1][j]) {
if(dfs(i - 1, j, index + 1)) {
visit[i][j] = 0;
return true;
}
}
if(j - 1 >= 0 && !visit[i][j - 1]) {
if(dfs(i, j - 1, index + 1)) {
visit[i][j] = 0;
return true;
}
}
visit[i][j] = 0;
return false;
}
bool exist(vector<vector<char>>& board, string word) {
vector<int> tmp;
matrix = board;
str = word;
visit.resize(matrix.size());
for(int i = 0; i < matrix.size(); i++) {
visit[i].resize(matrix[0].size());
}
for(int i = 0; i < matrix.size(); i++) {
for(int j = 0; j < matrix[0].size(); j++) {
if(matrix[i][j] == str[0]) {
if(dfs(i, j, 0)) {
return true;
}
}
}
}
return false;
}
};

BETTER SOLUTION

代码更简洁,且没有使用visit矩阵,而是将原矩阵的字符进行更改。但如果像我的代码这样先找到第一个匹配的字符再调用dfs,运行时间会更少。时间复杂度O(MN3^K),空间复杂度O(K)。

class Solution {
public:
bool exist(vector<vector<char>>& board, string word) {
rows = board.size();
cols = board[0].size();
for(int i = 0; i < rows; i++) {
for(int j = 0; j < cols; j++) {
if(dfs(board, word, i, j, 0)) return true;
}
}
return false;
}
private:
int rows, cols;
bool dfs(vector<vector<char>>& board, string word, int i, int j, int k) {
if(i >= rows || i < 0 || j >= cols || j < 0 || board[i][j] != word[k]) return false;
if(k == word.size() - 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 res;
}
};

SUMMARY

注意递归中回溯的设计,在递归前进行设置,最后递归调用结束后返回前还原设置,如题目中visit的设置。