题目描述
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。
矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
思路:
最直接的就是搜索了,走迷宫,但想了半个小时还有没有其他解法类似去重啥的,想不出看讨论是搜索。。。
然后开始前要做很多准备工作,打映输出,弄个坐标对象pos(x, y),节点对象(val, flag),将给的两个字符串转为map和list(真不友好)花了半个小时以上。。
然后开始走迷宫一开始被vector引导了朝广搜的思路走,但又没意识到还往深搜靠,结果函数定义的不好(传vector<node>),又去看了解答意识到了然后写以前最常写的方式,打标去标,又想起讨论区的回溯一说法加上刚才这种做法代码臃肿,于是尝试精简,写成了后续遍历(回溯),然后彩的一个坑就是xy代表行列和代表坐标的意义是相反的。然后发现这种做法除了可以判断是否可达还可以统计路线总数。花了两个小时。。。。。</node>
#include <iostream> #include <vector> using namespace std;
struct Node{ char val=' '; int flag=0; Node(char i){ val = i; flag = 0; } }; struct Pos{ int x; int y; Pos(int i, int j){ x=i; y=j; } }; class Solution { public: vector<vector<Node>> map; vector<char> vstr; /* void print() { for (int i = 0; i < map.size();++i) { for (int j = 0; j < map[0].size(); ++j) { cout << map[i][j].val << " "; } cout << endl; } }*/ int search(Pos pos, int index){ if(pos.x<0||pos.x==map.size()||pos.y<0||pos.y==map[0].size()) return 0; if(map[pos.x][pos.y].flag == 1 ||map[pos.x][pos.y].val != vstr[index]) return 0; if(index == vstr.size()-1) return 1; int sum = 0; map[pos.x][pos.y].flag = 1; sum+=search(Pos(pos.x-1, pos.y), index+1); //行列的xy与坐标的xy意义相反 sum+=search(Pos(pos.x, pos.y+1), index + 1); sum+=search(Pos(pos.x+1, pos.y), index + 1); sum+=search(Pos(pos.x, pos.y-1), index + 1); map[pos.x][pos.y].flag = 0; return sum; } bool hasPath(char* matrix, int rows, int cols, char* str) { for(int i=0;i<rows;++i){ //繁琐的初始化操作map vector<Node> row; for(int j=0;j<cols;++j){ row.push_back(Node(matrix[i*cols+j])); } map.push_back(row); } for(int i=0;str[i]!='\0';++i) { //转str为vector, vstr.push_back(str[i]); } int sum=0; for(int i=0;i<rows;++i){ for(int j=0;j<cols;++j){ if(map[i][j].val == vstr[0]){ sum+=search(Pos(i, j), 0); /// } } } return sum; } };
int main() { Solution s; char a[]={'a','b','c','e','s','f','c','s','a','d','e','e'}, b[] = "bcced"; s.hasPath(a, 3, 4, b); return 0; }
下面是之前那种最原始的想法实现(模拟搜索),两种的区别在:1.于回溯版本的是只关心当前节点,而下面这个版本是只关心下一个节点是否合法,合法才给进入,所以当前节点是默认合法的了(所以第一次调用要在外围打标除标)其实没回溯好。2.触底的判断这里是用一个块内全局变量来统计不是通过返回值,通过返回值则还是回溯,但代码结构上就特别臃肿了。花了又一个小时。。。
class Solution { public: vector<vector<Node>> map; vector<char> vstr; void print() { for (int i = 0; i < map.size();++i) { for (int j = 0; j < map[0].size(); ++j) { cout << map[i][j].val << " "; } cout << endl; } } int search2_sum = 0; //块内全局记录变量 void search2(Pos pos, int index){ if(index == vstr.size()-1) { search2_sum++; return; } if(pos.x-1>=0 && map[pos.x-1][pos.y].flag==0 && map[pos.x-1][pos.y].val==vstr[index+1]){ map[pos.x-1][pos.y].flag = 1; search2(Pos(pos.x-1, pos.y), index+1); map[pos.x-1][pos.y].flag = 0; } if(pos.y+1<map[0].size() && map[pos.x][pos.y+1].flag==0 && map[pos.x][pos.y+1].val==vstr[index+1]) { map[pos.x][pos.y+1].flag= 1; search2(Pos(pos.x, pos.y+1), index + 1); map[pos.x][pos.y+1].flag = 0; } if(pos.x+1<map.size() && map[pos.x+1][pos.y].flag==0 && map[pos.x+1][pos.y].val==vstr[index+1]) { map[pos.x+1][pos.y].flag = 1; search2(Pos(pos.x+1, pos.y), index + 1); map[pos.x+1][pos.y].flag = 0; } if(pos.x>=0 && map[pos.x][pos.y-1].flag==0 && map[pos.x][pos.y-1].val==vstr[index+1]) { map[pos.x][pos.y-1].flag = 1; search2(Pos(pos.x, pos.y-1), index + 1); map[pos.x][pos.y-1].flag = 0; } return ; } bool hasPath(char* matrix, int rows, int cols, char* str) { for(int i=0;i<rows;++i){ vector<Node> row; for(int j=0;j<cols;++j){ row.push_back(Node(matrix[i*cols+j])); } map.push_back(row); } for(int i=0;str[i]!='\0';++i) { vstr.push_back(str[i]); } int sum=0; for(int i=0;i<rows;++i){ for(int j=0;j<cols;++j){ if(map[i][j].val == vstr[0]){ map[i][j].flag = 1; //第一个节点打标保证进入 search2(Pos(i, j), 0); map[i][j].flag = 0; //除标 } } } return sum; } };