1 题目描述:
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如[a b c e s f c s a d e e]是3*4矩阵,其包含字符串"bcced"的路径,但是矩阵中不包含“abcb”路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
2 解题思路:利用回溯法求解。
首先,在矩阵中任选一个格子作为路径的起点。假设矩阵中某个格子的字符为ch并且这个格子对应于路径上的第i个字符。如果路径上第i个字符不是ch,那么这个格子就不可能处在路径上的第i个位置。如果路径上的第i个字符正好是ch,就往相邻的格子寻找路径上第i+1个字符。除了在矩阵边界上的格子之外,每个格子都有4个相邻的格子。重复这个过程直到路径上的所有字符都在矩阵中找到相应的位置。
当在矩阵中定位了路径上n个字符的位置后,在与第n个字符对应的格子周围都没有找到第n+1个字符,则只好在路径上回到第n-1个字符,重新去寻找第n个字符由于路径不能重复进入格子,我们需要定义一个字符矩阵一样大小的布尔值矩阵,用来标记路径是否已经进入了相应格子。
注:关于回溯法有模板,可以解决一类问题,回头自己要整理一下理解并掌握用好回溯法。
3 代码实现:
class Solution {
public:
bool hasPath(char* matrix, int rows, int cols, char* str)
{
if(matrix==nullptr||rows<1||cols<1||str==nullptr)
return false;
int Pathlength=0; //递增索引指示待匹配的字符
bool *visited=new bool [rows*cols]; //标志数组记录每个格子是否使用过
memset(visited,0,rows*cols); //内存初始化函数:初始化为0
for(int row=0;row<rows;row++)
{
for(int col=0;col<cols;col++)
{
//flag=hasPathCore(matrix,rows,row,cols,col,str,Pathlength,visited); //从每个格子为起始位置进行回溯求解
if(hasPathCore(matrix,rows,row,cols,col,str,Pathlength,visited)) //只要找到一个解就返回true,所以上面的一句有问题,
return true;
}
}
delete [] visited;
return false;
}
bool hasPathCore(char* matrix,int rows,int row,int cols,int col,char* str,int& Pathlength,bool* visited)
{
if(str[Pathlength]=='\0') //这句很重要,是回溯的终止条件
return true;
bool hasPath_=false;
// if(row<rows && row>=0 && col<cols && col>=0 && matrix[row*rows+col]==str[Pathlength] && !visited[row*rows+col]) 这里 出错了!!!
if(row<rows && row>=0 && col<cols && col>=0 && matrix[row*cols+col]==str[Pathlength] && !visited[row*cols+col])
{
++Pathlength;
visited[row*cols+col]=true;
hasPath_=hasPathCore(matrix,rows,row-1,cols,col,str,Pathlength,visited)||hasPathCore(matrix,rows,row,cols,col-1,str,Pathlength,visited)||hasPathCore(matrix,rows,row+1,cols,col,str,Pathlength,visited)||hasPathCore(matrix,rows,row,cols,col+1,str,Pathlength,visited);
if(!hasPath_)
{
--Pathlength;
visited[row*cols+col]=false;
}
}
return hasPath_;
}
};
4 问题分析:
记录下自己的调试分析,发现一个问题,要提前解决掉,就是在牛客网的平台上自己的代码能够提交测试通过,然而自己在vs2010中调试却不通过(震惊!自己的基础还是不行,字符数组定义这块出问题了!!!)所以,不仅要会在封装好接口的平台上写核心算法,还要会写出整个算法程序:包含头文件、主函数、类模块(算法)、以及测试用例。因为在华为笔试的时候是要写出包含头文件在内的完整代码,这里考验的就是基础编程能力,有很多细节问题需要注意。
#include<iostream>
#include<stdio.h>
#include<cstring>
using namespace std;
class Solution {
public:
static bool hasPath(char* matrix, int rows, int cols, char* str)
{
if(matrix==nullptr||rows<1||cols<1||str==nullptr)
return false;
int Pathlength=0; //递增索引指示待匹配的字符
bool *visited=new bool [rows*cols]; //标志数组记录每个格子是否使用过
memset(visited,0,rows*cols); //内存初始化函数:初始化为0
for(int row=0;row<rows;row++)
{
for(int col=0;col<cols;col++)
{
//flag=hasPathCore(matrix,rows,row,cols,col,str,Pathlength,visited); //从每个格子为起始位置进行回溯求解
if(hasPathCore(matrix,rows,row,cols,col,str,Pathlength,visited)) //只要找到一个解就返回true,所以上面的一句有问题,
return true;
}
}
delete [] visited;
return false;
}
private:
static bool hasPathCore(char* matrix,int rows,int row,int cols,int col,char* str,int& Pathlength,bool* visited)
{
if(str[Pathlength]=='\0') //这句很重要,是回溯的终止条件
return true;
bool hasPath_=false;
// if(row<rows && row>=0 && col<cols && col>=0 && matrix[row*rows+col]==str[Pathlength] && !visited[row*rows+col]) 这里 出错了!!!
if(row<rows && row>=0 && col<cols && col>=0 && matrix[row*cols+col]==str[Pathlength] && !visited[row*cols+col])
{
++Pathlength;
visited[row*cols+col]=true;
hasPath_=hasPathCore(matrix,rows,row-1,cols,col,str,Pathlength,visited)||hasPathCore(matrix,rows,row,cols,col-1,str,Pathlength,visited)||hasPathCore(matrix,rows,row+1,cols,col,str,Pathlength,visited)||hasPathCore(matrix,rows,row,cols,col+1,str,Pathlength,visited);
if(!hasPath_)
{
--Pathlength;
visited[row*cols+col]=false;
}
}
return hasPath_;
}
};
//bool hasPathCore(char* matrix,int rows,int row,int cols,int col,char* str,int& Pathlength,bool* visited);
//bool hasPath(char* matrix, int rows, int cols, char* str)
// {
// if(matrix==nullptr||rows<1||cols<1||str==nullptr)
// return false;
// int Pathlength=0; //递增索引指示待匹配的字符
// bool *visited=new bool [rows*cols]; //标志数组记录每个格子是否使用过
// memset(visited,0,rows*cols); //内存初始化函数:初始化为0
// for(int row=0;row<rows;row++)
// {
// for(int col=0;col<cols;col++)
// {
// //flag=hasPathCore(matrix,rows,row,cols,col,str,Pathlength,visited); //从每个格子为起始位置进行回溯求解
// if(hasPathCore(matrix,rows,row,cols,col,str,Pathlength,visited)) //只要找到一个解就返回true,所以上面的一句有问题,
// return true;
// }
// }
// delete [] visited;
// return false;
// }
//bool hasPathCore(char* matrix,int rows,int row,int cols,int col,char* str,int& Pathlength,bool* visited)
// {
// if(str[Pathlength]=='\0') //这句很重要,是回溯的终止条件
// return true;
// bool hasPath_=false;
// // if(row<rows && row>=0 && col<cols && col>=0 && matrix[row*rows+col]==str[Pathlength] && !visited[row*rows+col]) 这里 出错了!!!
// if(row<rows && row>=0 && col<cols && col>=0 && matrix[row*cols+col]==str[Pathlength] && !visited[row*cols+col])
// {
// ++Pathlength;
// visited[row*cols+col]=true;
// hasPath_=hasPathCore(matrix,rows,row-1,cols,col,str,Pathlength,visited)||hasPathCore(matrix,rows,row,cols,col-1,str,Pathlength,visited)||hasPathCore(matrix,rows,row+1,cols,col,str,Pathlength,visited)||hasPathCore(matrix,rows,row,cols,col+1,str,Pathlength,visited);
// if(!hasPath_)
// {
// --Pathlength;
// visited[row*cols+col]=false;
// }
// }
// return hasPath_;
// }
int main()
{
//Test1:(错误的赋值)
char a[]={'A','B','C','E','S','F','C','S','A','D','E','E'}; //这里为什么不对?因为这种字符串赋值末尾还有个'\0',实际字符串长度不是12,而是13.
char str[]={'B','C','C','E','D'}; //这种字符串初始化方式,不会自动在末尾加'\0',而我们要求的字符串操作一般是以'\0'
cout<<Solution::hasPath(a,3,4,str)<<endl; //作为结束字符串结束的标志的,没有'\0'结束符,我们操作的时候不知道字符串的末尾是什么
//修正Test1:
char a[]={'A','B','C','E','S','F','C','S','A','D','E','E','\0'};
char str[]={'B','C','C','E','D','\0'};
cout<<Solution::hasPath(a,3,4,str)<<endl;
//Test2:
/*char a[]="ABCESFCSADEE";
char str[]="BCCED";
cout<<Solution::hasPath(a,3,4,str)<<endl;*/
//通过测试发现上述Test1有问题,本来应该测试通过的,结果Test1不通过,然鹅Test2却通过。
//Test3:
/* char* matrix = "ABTGCFCSJDEH";
char* str = "BFCE";
cout<<hasPath(matrix,3,4,str)<<endl;*/
//Test4:
/* char matrix[] = "ABTGCFCSJDEH";
char str[] = "BFCE";
cout<<hasPath(matrix,3,4,str)<<endl;*/
return 0;
}
学习:上面的测试过程出问题的关键就是自己在构造测试用例的时候,因为对字符数组的初始化及赋值操作基础掌握不好,而导致的错误,因此也学习了字符数组的初始化及赋值操作。
1.字符数组初始化
在C语言中,字符串是当做字符数组来处理的;所以字符串有两种声明方式,一种是字符数组,一种是字符指针。
字符数组初始化
1 char parr[] = "zifuchuanshuzu";
2 char charr[] = { 'z','i','f','u','c','h','u','a','n','s','h','u','z','u' };
这是字符数组初始化的两种方式,但是这两种方式其实是不等价的;他们的数组长度不同。
2.字符指针
在C语言中我们也可以使用字符指针来存储字符串。
- 字符指针初始化
char* str="zifuchuanshuzu";
关于字符数组初始化及赋值见博客https://blog.csdn.net/u011028771/article/details/52622721