题目描述
描述:请设计一个函数,用来判断在一个n乘m的矩阵中是否存在一条包含某长度为len的字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如
abce
sfcs
adee
矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
数据范围: 0≤n,m≤20 ,1≤len≤25 。
输入:[[a,b,c,e],[s,f,c,s],[a,d,e,e]],"abcced"
返回值:true
输入:[[a,b,c,e],[s,f,c,s],[a,d,e,e]],"abcb"
返回值:false
解题思路
矩阵中的路径:最直观的想法是,首先使用stxy变量存放矩阵matrix中出现的所有word起始位置元素的下标,然后以这些下标为起始开始dfs遍历,并使用tfxy变量存放矩阵matrix中的元素是否被访问,判断是否存在一条字符串路径,如果存在则直接返回true,如果所有均遍历完后仍不存在,则返回false。其中dfs遍历是这样的,首先判断字符串word是否已经访问结束,如果是则返回true,然后判断矩阵下标是否合法,如果不合法则返回false,接着判断矩阵元素是否等于字符串元素,如果是则记录元素已访问,并且返回上下左右四个方向的遍历。
(以下这个代码是最原始的思路写的,有一个案例无法通过)
bool dfs(vector<vector<char>>& matrix, vector<vector<bool>>& tfxy, int i, int j,
string& word, int index) {
if (index >= word.size()) //先要处理这个判断条件 否则[['A','A'],['A','A']] "AAAA" 就会出现错误
return true;
if (i < 0 || i >= matrix.size() || j < 0 || j >= matrix[0].size() ||
tfxy[i][j] == true) //处理非法下标
return false;
if (word[index] == matrix[i][j]) {
tfxy[i][j] = true;
return dfs(matrix, tfxy, i + 1, j, word, index + 1) ||
dfs(matrix, tfxy, i - 1, j, word, index + 1) ||
dfs(matrix, tfxy, i, j + 1, word, index + 1) ||
dfs(matrix, tfxy, i, j - 1, word, index + 1);
} else
return false; //不相等返回false
return false;
}
bool hasPath(vector<vector<char> >& matrix, string word) {
int n = matrix.size(), m = matrix[0].size(), len = word.size();
if (len > n * m) return false;
char start = word[0];
vector<vector<int>> stxy;
for (int i = 0; i < n; i++) //寻找起始节点
for (int j = 0; j < m; j++)
if (matrix[i][j] == start)
stxy.push_back({i, j});
for (auto stxys : stxy) {
vector<vector<bool>> tfxy(n, vector<bool>(m, false)); //标记是否访问
bool flag = dfs(matrix, tfxy, stxys[0], stxys[1], word, 0);
if (flag == true) return true;
}
return false;
}
问题:但是这里存在几个问题:第一个是字符串word是否已经访问结束和矩阵下标是否合法的顺序不能反,否则[[‘A’,‘A’],[‘A’,‘A’]] "AAAA"就会出现错误;第二个是[[A,B,C],[B,E,D],[F,G,G]],"ABCDEBF"或者[[A,B,F],[B,E,G],[C,D,G]],"ABCDEBF"两个案例无法通过,这就让我想起来,如果某一个元素其有多个方向是相同的下一个元素,但是按照我上述的方法dfs遍历后,如果先遍历的那一个方向是一个死胡同,那么程序是直接返回false的,但是事实上,我还可以回到原来的元素进行下一个方向的遍历,这明显是回溯法,于是最后通过的程序如下。
(以下这个代码是按照改进的思路优化写的,案例全部通过)
bool dfs(vector<vector<char>>& matrix, vector<vector<bool>>& tfxy, int i, int j,string& word, int index)
{
if (index >= word.size())
//先要处理这个判断条件 否则[['A','A'],['A','A']] "AAAA" 就会出现错误
return true;
if (i < 0 || i >= matrix.size() || j < 0 || j >= matrix[0].size()||
matrix[i][j] !=word[index]||tfxy[i][j] == true) //处理非法下标
return false;
tfxy[i][j] = true;
bool res=dfs(matrix, tfxy, i, j + 1, word, index + 1) ||
dfs(matrix, tfxy, i + 1, j, word, index + 1) ||
dfs(matrix, tfxy, i - 1, j, word, index + 1)||
dfs(matrix, tfxy, i, j - 1, word, index + 1);
tfxy[i][j]=false;
//利用回溯法 否则[[A,B,C],[B,E,D],[F,G,G]],"ABCDEBF"会出错
//[[A,B,F],[B,E,G],[C,D,G]],"ABCDEBF"
return res;
}
bool hasPath(vector<vector<char> >& matrix, string word) {
int n = matrix.size(), m = matrix[0].size(), len = word.size();
if (len > n * m) return false;
char start = word[0];
vector<vector<int>> stxy;
for (int i = 0; i < n; i++) //寻找起始节点
for (int j = 0; j < m; j++)
if (matrix[i][j] == start)
stxy.push_back({i, j});
for (auto stxys : stxy)
{
vector<vector<bool>> tfxy(n, vector<bool>(m, false)); //标记是否访问
bool flag = dfs(matrix, tfxy, stxys[0], stxys[1], word, 0);
if (flag == true) return true;
}
return false;
}
总结:返回true的写在一起,返回false的写在一起,注意先后顺序。任何事情都不是一蹴而就的,先有思路,再去实践,接着分析,不断优化。