【leetcode】【medium】79. Word Search

戏子无情 提交于 2020-02-28 18:51:05

79. Word Search

Given a 2D board and a word, find if the word exists in the grid.

The word can be constructed from letters of sequentially adjacent cell, where "adjacent" cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once.

Example:

board =
[
  ['A','B','C','E'],
  ['S','F','C','S'],
  ['A','D','E','E']
]

Given word = "ABCCED", return true.
Given word = "SEE", return true.
Given word = "ABCB", return false.

题目链接:https://leetcode-cn.com/problems/word-search/

 

思路

这道题很简单,但是写出来的代码一直在运行时间上表现不好。

第一版代码是这样的:

class Solution {
public:
    vector<vector<bool>> used;
    bool exist(vector<vector<char>>& board, string word) {
        if(board.size()==0 || word.size()==0) return false;
        int num = board.size(), len = board[0].size();
        vector<bool> tmp(len, false);
        used = vector<vector<bool>>(num, tmp);
        for(int i=0; i<num; ++i){
            for(int j=0; j<len; ++j){
                if(board[i][j]==word[0]){
                    if(word.size()== 1) return true;
                    used[i][j] = true;
                    bool up=false, down=false, left=false, right = false;
                    string nword = word.substr(1);
                    if(i-1>=0) up = trace(board, nword, i-1, j);
                    if(!up && j-1>=0) left = trace(board, nword, i, j-1);
                    if(!up && !left && i+1<num) down = trace(board, nword, i+1, j);
                    if(!up && !left && !down && j+1<len) right = trace(board, nword, i, j+1);
                    if(up || down || left || right) return true;
                    used[i][j] = false;
                }
            }
        }
       return false;
    }
    bool trace(vector<vector<char>> board, string word, int i, int j){
        int num = board.size(), len = board[0].size();
        if(!used[i][j] && board[i][j]==word[0]){
            if(word.size()==1) return true;
            used[i][j] = true;
            bool up=false, down=false, left=false, right = false;
            string nword = word.substr(1);
            if(i-1>=0) up = trace(board, nword, i-1, j);
            if(!up && j-1>=0) left = trace(board, nword, i, j-1);
            if(!up && !left && i+1<num) down = trace(board, nword, i+1, j);
            if(!up && !left && !down && j+1<len) right = trace(board, nword, i, j+1);
            if(up || down || left || right) return true;
            used[i][j] = false;
        }
        return false;
    }
};

运行时间1456 ms,内存546.6 MB,只超过5%用户。

(接下来的修改就一直在超时,超时样例:)

[["a","a","a","a"],["a","a","a","a"],["a","a","a","a"],["a","a","a","a"],["a","a","a","b"]]
"aaaaaaaaaaaaaaaaaaaa"

没看出代码慢在哪里,于是先想把代码简化一下,结果以下代码就直接运行超时了: 

class Solution {
public:
    vector<vector<bool>> used;
    bool exist(vector<vector<char>>& board, string word) {
        if(board.size()==0 || word.size()==0) return false;
        int num = board.size(), len = board[0].size();
        vector<bool> tmp(len, false);
        used = vector<vector<bool>>(num, tmp);
        for(int i=0; i<num; ++i){
            for(int j=0; j<len; ++j){
                if(trace(board, word, i, j)) return true;
            }
        }
       return false;
    }
    bool trace(vector<vector<char>> board, string word, int i, int j){
        int num = board.size(), len = board[0].size();
        if(!used[i][j] && board[i][j]==word[0]){
            if(word.size()==1) return true;
            used[i][j] = true;
            bool up=false, down=false, left=false, right = false;
            string nword = word.substr(1);
            if(i-1>=0) up = trace(board, nword, i-1, j);
            if(!up && j-1>=0) left = trace(board, nword, i, j-1);
            if(!up && !left && i+1<num) down = trace(board, nword, i+1, j);
            if(!up && !left && !down && j+1<len) right = trace(board, nword, i, j+1);
            if(up || down || left || right) return true;
            used[i][j] = false;
        }
        return false;
    }
};

去掉used,减少数据读取操作,仍然超时:

class Solution {
public:
    bool exist(vector<vector<char>>& board, string word) {
        int num = board.size();
        if(!num) return false;
        int len = board[0].size();
        bool res = false;
        for(int i=0; i<num; ++i){
            for(int j=0; j<len; ++j){
                res = trace(board, word, i, j, 0);
                if(res) return true;
            }
        }
       return false;
    }
    bool trace(vector<vector<char>> board, string word, int i, int j, int idx){
        int num = board.size(), len = board[0].size();
        if(board[i][j]==word[idx]){
            if(word.size()-1==idx) return true;
            board[i][j] = '\n';
            bool res = false;
            if(i-1>=0) {
                res = trace(board, word, i-1, j, idx+1);
                if(res) return true;
            }
            if(j-1>=0){
                res = trace(board, word, i, j-1, idx+1);
                if(res) return true;
            }
            if(i+1<num) {
                res = trace(board, word, i+1, j, idx+1);
                if(res) return true;
            }
            if(j+1<len) {
                res = trace(board, word, i, j+1, idx+1);
                if(res) return true;
            }
            board[i][j] = word[idx];
        }
        return false;
    }
};

最终看了别人的代码,理解后重写:

class Solution {
public:
    bool exist(vector<vector<char>>& board, string word) {
        int row = board.size();
        if(!row) return false;
        int col = board[0].size();
        bool res = false;
        for(int i = 0; i < row; i++) {
            for(int j = 0; j < col; j++) {
                res = dfs(board, i, j, word, 0);
                if(res == true) return true;
            }
        }
        return false;
    }
    bool dfs(vector<vector<char>> & board, int row, int col, string & word, int index) {
        int maxRow = board.size();
        int maxCol = board[0].size();
        if(board[row][col] == word[index]){
            if(index == word.length() - 1) {
                return true;
            }
            board[row][col] = '\n';
            bool rc = false;
            if(row + 1 < maxRow) {
                rc = dfs(board, row + 1, col, word, index + 1);
                if(rc) return true;
            }
            if(row - 1 >= 0 ) {
                rc = dfs(board, row - 1, col, word, index + 1);
                if(rc) return true;
            }
            if(col + 1 < maxCol ) {
                rc = dfs(board, row, col + 1, word, index + 1);
                if(rc) return true;
            }
            if(col - 1 >= 0 ) {
                rc = dfs(board, row, col - 1, word, index + 1);
                if(rc) return true;
            }
            board[row][col] = word[index];
        }
        return false;
    }
};

运行时间24 ms,内存10.3 MB,超过75%和75%。

别人的代码:https://leetcode-cn.com/problems/word-search/solution/tu-de-dfssou-suo-jie-fa-by-xuqnqn/

class Solution {
public:
    bool dfs(vector<vector<char>> & board, int row, int col, string & word, int index) {
        int maxRow = board.size();
        int maxCol = board[0].size();
        if(index == word.length() - 1) {
            return true;
        }
        board[row][col] = '\n';
        bool rc = false;
        if(row + 1 < maxRow && board[row + 1][col] == word[index + 1]) {
            rc = dfs(board, row + 1, col, word, index + 1);
            if(rc) return true;
        }
        if(row - 1 >= 0 && board[row - 1][col] == word[index + 1]) {
            rc = dfs(board, row - 1, col, word, index + 1);
            if(rc) return true;
        }
        if(col + 1 < maxCol && board[row][col + 1] == word[index + 1]) {
            rc = dfs(board, row, col + 1, word, index + 1);
            if(rc) return true;
        }
        if(col - 1 >= 0 && board[row][col - 1] == word[index + 1]) {
            rc = dfs(board, row, col - 1, word, index + 1);
            if(rc) return true;
        }
        board[row][col] = word[index];
        return false;
    }
    bool exist(vector<vector<char>>& board, string word) {
        int row = board.size();
        if(!row) return false;
        int col = board[0].size();
        bool res = false;
        for(int i = 0; i < row; i++) {
            for(int j = 0; j < col; j++) {
                if(board[i][j] == word[0]) {
                    res = dfs(board, i, j, word, 0);
                    if(res == true) return true;
                }
            }
        }
        return false;
    }
};

运行时间:20ms,内存10.3MB,超过95%和75%。

为了找到原因,分别计算了最后两个代码调用递归函数的次数,分别是343898 和 343917,在递归次数上没有太大区别。

这找bug的过程真的很久。。。

最后终于发现,问题出在了传参上。。。。

引用传参在递归时不需要新建空间和复制操作。。。。

之前做回溯法的题没有出现,是一维变量,怎么递归都还好,频繁的复制操作还不至于影响太大,现在到二维空间了,搜寻空间突然暴增,于是就。。

不过还好在遇到的第一个二维空间题就找到了这个问题所在,否则之后每次做二维空间都要爆炸。。

——————————————————————

本题这么简单却用了这么久,总结了一下就是:

1)在递归内部尽量不要使用全局变量标记是否处理成功,尽量通过返回值去标记。

2)减少不必要的读取。在board上直接改变减少了数据查询,也能节省点效率。

3)能在上一层判断就先判断,传进下一层递归函数,就算判断完马上返回,数据多了时间也会有些差别。

4)last but not least!:引用传参效率大于复制传参,如果递归次数很大 + 参数占用空间也不小的话,最好用引用传参,然后避免误改了就行。

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!