题目描述
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。
例如下面的矩阵包含了一条 bfce 路径。但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
测试用例
- 功能测试(在多行多列的矩阵中存在或者不存在路径)
- 边界值测试(矩阵只有一行或者只有一列;矩阵和路径中的所有字母都是相同的)
- 特殊输入测试(输入空指针)
题目考点
- 考察应聘者对回溯法的理解。通常在二维矩阵上找路径这类问题都可以应用回溯法解决。
- 考察应聘者对数组的编程能力。我们一般都把矩阵看成一个二维数组。只有对数组的特性充分了解,只有可能快速、正确得实现回溯法的代码。
解题思路
探讨:
这是一个可以用回朔法(见补充)解决的典型题。
首先,在矩阵中任选一个格子作为路径的起点。
由于回朔法的递归特性,路径可以被看成一个栈。当在矩阵中定位了路径中前n个字符的位置之后,在与第n个字符对应的格子的周围都没有找到第n+1个字符,这个时候只要在路径上回到第n-1个字符,重新定位第n个字符。一直重复这个过程,直到路径字符串上所有字符都在矩阵中找到合适的位置。
由于路径不能重复进入矩阵的格子,还需要定义和字符矩阵大小一样的布尔值矩阵,用来标识路径是否已经进入每个格子。
思路:
本问题是典型的矩阵搜索问题,可使用 深度优先搜索(DFS)+ 剪枝 解决。
深度优先搜索: 可以理解为暴力法遍历矩阵中所有字符串可能性。DFS 通过递归,先朝一个方向搜到底,再回溯至上个节点,沿另一个方向搜索,以此类推。
剪枝: 在搜索中,遇到 这条路不可能和目标字符串匹配成功 的情况(例如:此矩阵元素和目标字符不同、此元素已被访问),则应立即返回,称之为 可行性剪枝 。
代码
package com.offer._12;
/**
* @author :jhys
* @date :Created in 2020/11/28 16:57
* @Description :
*/
public class Solution {
public boolean exist(char[][] board, String word) {
char[] words = word.toCharArray();
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[0].length; j++) {
if (dfs(board, words, i, j, 0)) {
return true;
}
}
}
return false;
}
boolean dfs(char[][] board, char[] word, int i, int j, int k) {
if (i >= board.length || i < 0 || j >= board[0].length || j < 0 || board[i][j] != word[k]) {
return false;
}
if ( k == word.length - 1) {
return true;
}
board[i][j] = '\0';
boolean 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;
}
}