题目链接
题目描述
判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向上下左右移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。
例如下面的矩阵包含了一条 bfce 路径。
解题思路(回溯法+DFS+递归+剪枝)
使用回溯法(backtracking)进行求解,它是一种暴力搜索方法,通过搜索所有可能的结果来求解问题。回溯法在一次搜索结束时需要进行回溯(回退),将这一次搜索过程中设置的状态进行清除,从而开始一次新的搜索过程。例如下图示例中,从 f 开始,下一步有 4 种搜索可能,如果先搜索 b,需要将 b 标记为已经使用,防止重复使用。在这一次搜索结束之后,需要将 b 的已经使用状态清除,并搜索 c。
- 深度优先搜索: 可以理解为暴力法遍历矩阵中所有字符串可能性。DFS 通过递归,先朝一个方向搜到底,再回溯至上个节点,沿另一个方向搜索,以此类推。
- 剪枝: 在搜索中,遇到
这条路不可能和目标字符串匹配成功
的情况(例如:此矩阵元素和目标字符不同、此元素已被访问),则应立即返回,称之为可行性剪枝
。
dfs回溯过程
dfs(){
// 第一步,检查下标
// 第二步:检查是否被访问过,或者是否满足当前匹配条件
// 第三步:检查是否满足返回结果条件
// 第四步:都没有返回,说明应该进行下一步递归
// 标记
dfs(下一次)
// 回溯
}
int main() {
dfs(0, 0);
}
DFS 解析:
- 递归参数: 当前元素在矩阵
board
中的行列索引i
和j
,当前目标字符在word
中的索引k
。 - 终止条件:
- 返回 false : (1) 行或列索引越界 或 (2) 当前矩阵元素与目标字符不同 或 (3) 当前矩阵元素已访问过 ( (3) 可合并至 (2) ) 。
- 返回 true :
k = len(word) - 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) {
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; // 越界或当前矩阵元素与目标字符不同或当前矩阵元素已访问过,则为false
// 当前矩阵元素已访问过,标记当前矩阵元素: 将 board[i][j] 修改为'\0'
// 所以判断条件和当前矩阵元素与目标字符不同一样
if(k == word.size() - 1) return true; // k = len(word) - 1 ,即字符串 word 已全部匹配
board[i][j] = '\0'; // 当前矩阵元素已访问过,标记当前矩阵元素: 将 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]; // 还原当前矩阵元素: 将 board[i][j] 元素还原至初始值,即 word[k]
return res;
}
};
时间复杂度 O(3^KMN):最差情况下,需要遍历矩阵中长度为 KK 字符串的所有方案,时间复杂度为 O(3^K);矩阵中共有 MN 个起点,时间复杂度为 O(MN) 。
- 空间复杂度 O(K) : 搜索过程中的递归深度不超过 K ,因此系统因函数调用累计使用的栈空间占用 O(K)O (因为函数返回后,系统调用的栈空间会释放)。最坏情况下 K = MN ,递归深度为 MN ,此时系统栈使用 O(MN) 的额外空间。
**