《剑指offer》——面试题12. 矩阵中的路径

本文介绍了一种使用回溯法在矩阵中查找特定字符串路径的算法。通过实例演示了如何从矩阵任意位置开始,向上下左右移动,寻找指定字符串的所有字符路径,确保路径不重复经过同一格。

难度:中等

题目

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如,在下面的3×4的矩阵中包含一条字符串“bfce”的路径(路径中的字母用加粗标出)。

[[“a”,“b”,“c”,“e”],
[“s”,“f”,“c”,“s”],
[“a”,“d”,“e”,“e”]]

但矩阵中不包含字符串“abfb”的路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入这个格子。

示例 1:

输入:board = [[“A”,“B”,“C”,“E”],[“S”,“F”,“C”,“S”],[“A”,“D”,“E”,“E”]], word = “ABCCED”
输出:true
示例 2:

输入:board = [[“a”,“b”],[“c”,“d”]], word = “abcd”
输出:false
提示:

1 <= board.length <= 200
1 <= board[i].length <= 200

解答

思路:回溯法,不用递归所以代码比较长。有4个状态需要保存,分别是已找到点的x,y,这个点在上一个点的哪个方向,已经找到了几个点(这个可以从第一或第二个状态退出来,可以优化)。

序号步骤
1找到第一个点的位置
2在上一个点的周围寻找
3找不到回溯,找的到则继续寻找下一个点
序号知识点
1二维数组初始化vector<vector> used(a,vector(b,0)
2|和||的区别,|前后两个条件都要执行,||当前面一个成立时,后面那个条件不执行
3stack的使用pop,push,top
4switch case的使用
复杂度O
时间复杂度
空间复杂度O(n2)

代码

class Solution {
private:
    int firstRow=0,firstColumn=0;
public:
    bool exist(vector<vector<char>>& board, string word) {
        // 回溯
        if(!word.size()){
            return true;
        }
        if(!board.size()){
            return false;
        }
        
        const int boardRow=board.size();
        const int boardColumn=board[0].size();
        const int size=word.size();

        int index=0;
        stack<int> myrow;
        stack<int> mycolumn;
        vector<vector<char>> used(board.size(),vector<char>(board[0].size(),0));//保存上次在哪个方向找到的 1 2 3 4 分别代表左上右下
        // 找到第一个
        bool isFindFirst=false;
        while(index<size){
            if(!isFindFirst){
                isFindFirst=isFindFirstPoint(board,word[0],myrow,mycolumn,used);
                if(!isFindFirst){
                    return false;
                }
                else{
                    index++;
                    continue;
                }
            }
            // 栈顶四个方向搜索 左上右下
            if(isFindNextPoint(board, word[index],myrow,mycolumn,used)){
                index++;
            }
            else{
                index--;
            }
            if(index==0){
                isFindFirst=false;//重新找起点
            }
        }
        return true;
    }
    bool isFindNextPoint(const vector<vector<char>>& board, const char & Point,stack<int>& row,stack<int>& column,vector<vector<char>>& used){
        int topRow=row.top(),topColumn=column.top();
        switch(++used[topRow][topColumn]){
            case 1:
                if(isValidAndEqual(board,Point,topRow,topColumn-1,used)){
                    row.push(topRow);
                    column.push(topColumn-1);
                    used[topRow][topColumn]=1;
                    break;
                }
            case 2:
                if(isValidAndEqual(board,Point,topRow-1,topColumn,used)){
                    row.push(topRow-1);
                    column.push(topColumn);
                    used[topRow][topColumn]=2;
                    break;
                }
            case 3:
                if(isValidAndEqual(board,Point,topRow,topColumn+1,used)){
                    row.push(topRow);
                    column.push(topColumn+1);
                    used[topRow][topColumn]=3;
                    break;
                }
            case 4:
                if(isValidAndEqual(board,Point,topRow+1,topColumn,used)){
                    row.push(topRow+1);
                    column.push(topColumn);
                    used[topRow][topColumn]=4;
                    break;
                }
            default:
                // 这个点没用回溯
                row.pop();
                column.pop();
                used[topRow][topColumn]=0;
                return false;
        }
        return true;
    }

    bool isValidAndEqual(const vector<vector<char>>& board,const char & Point,int nowRow,int nowColumn,vector<vector<char>>& used){
        // cout<<"1: findNextPoint"<<nowRow<<" "<<nowColumn<<" "<<Point<<endl;
        if(nowRow<0||nowRow>=board.size()||nowColumn<0||nowColumn>=board[0].size()||used[nowRow][nowColumn]!=0){
            return false;
        }
        if(board[nowRow][nowColumn]==Point){
            // cout<<"findNextPoint"<<nowRow<<" "<<nowColumn<<" "<<Point<<endl;
            return true;
        }
        return false;
    }


    bool isFindFirstPoint(const vector<vector<char>>& board, const char & firstPoint,stack<int>& row,stack<int>& column,vector<vector<char>>& used){
        
        cout<<"firstRow"<<firstRow<<firstColumn<<endl;
        const int boardRow=board.size();
        const int boardColumn=board[0].size();       
        while(1){
            if(firstColumn>=boardColumn){
                firstRow++;
                firstColumn=0;
            }
            if(firstRow>=boardRow){
                break;
            }
            if(board[firstRow][firstColumn]==firstPoint){
                row.push(firstRow);
                column.push(firstColumn);
                // cout<<"firstRowColumn"<<firstRow<<" "<<firstColumn<<endl;
                firstColumn++;//防止下次进来找到同一个点
                return true;
            }
            firstColumn++;           
        }
        return false;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值