79. Word Search

104 阅读4分钟

题目描述

image.png

思路

  • DFS
  • DFS的过程中,如果当前char能对的上word中的char,则继续dfs,否则停止。
  • 当凑齐了word,返回true。怎么判断凑齐了?dfs函数中传一个k来记录index。

image.png

Visited array, early pruning

class Solution {
    public boolean exist(char[][] board, String word) {
        int m = board.length, n = board[0].length;
        if (canPruneEarly(board, word)) {
            return false;
        }
        boolean[][] visited = new boolean[m][n];
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (board[i][j] == word.charAt(0)) {
                    if (dfs(board, i, j, 0, word, visited)) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    public boolean dfs(char[][] board, int i, int j, int cur, String word, boolean[][] visited) {
        int m = board.length, n = board[0].length;
        if (i >= m || i < 0 || j >= n || j < 0) {
            return false;
        }

        if (visited[i][j]) {
            return false;
        }

        if (board[i][j] != word.charAt(cur)) {
            return false;
        }

        if (cur == word.length() - 1) {
            return true;
        }

        // On this path, I’m using cell (i,j) so it can’t be used again until we finish exploring this path.”
        visited[i][j] = true;

        boolean a = dfs(board, i + 1, j, cur + 1, word, visited);
        boolean b = dfs(board, i - 1, j, cur + 1, word, visited);
        boolean c = dfs(board, i, j + 1, cur + 1, word, visited);
        boolean d = dfs(board, i, j - 1, cur + 1, word, visited);

        //  after checking all neighbors, you do visited[i][j] = false; to “undo” that decision so that other searches (other paths branching from previous cells) can reuse (i,j) if it’s valid in a different path.
        visited[i][j] = false;

        return (a || b || c || d);
    }

    // Example pseudo-code for frequency pruning:
    private boolean canPruneEarly(char[][] board, String word) {
        int m = board.length, n = board[0].length;

        // 1) Quick size check:
        if (m * n < word.length()) {
            return true;  // we can prune => impossible
        }

        // 2) Count frequencies:
        int[] freqBoard = new int[128];  // or use a Map<Character, Integer>
        int[] freqWord = new int[128];

        // Fill freqBoard
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                freqBoard[board[i][j]]++;
            }
        }
        // Fill freqWord
        for (char c : word.toCharArray()) {
            freqWord[c]++;
        }

        // Compare
        for (int c = 0; c < 128; c++) {
            if (freqWord[c] > freqBoard[c]) {
                return true;  // prune => impossible
            }
        }

        // If we get here, no early prune
        return false;
    }

}

剪枝

class Solution {
    public boolean exist(char[][] board, String word) {
        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board[0].length; j++) {//每个格子都要判断
                if (dfs(board, i, j, word, 0)) {
                    return true;//只要从某个格子开始深搜能连城一串word,就说明能找到
                }
            }
        }
        return false;
    }
    //ij为格子坐标,k表示当前将要凑齐的word最大索引
    public boolean dfs(char[][] board, int i, int j, String s, int k) {
        int n = board.length, m = board[0].length;
        if (i > n - 1 || i < 0 || j > m - 1 || j < 0 || board[i][j] != s.charAt(k)) {
            return false;
        }
        if (k == s.length() - 1) {//找到了
            return true;
        }
        board[i][j] = '*';//防止重复访问,下一个节点的lrud可能覆盖到这个点,比如找ABCB会返回true
        //这里暗含回溯:当前处在[row,col],选择[row+1,col]继续递归,返回false的话,会撤销[row+1,col]这个选择,回到[row,col],选择[row-1,col]继续递归。
        //剪枝:只要其中有一个方向上的递归调用返回真,||后的递归就不会执行,即找到解就终止搜索,利用||的短路效应,把枝剪了。
        boolean flag = (
            dfs(board, i, j + 1, s, k + 1) || 
            dfs(board, i, j - 1, s, k + 1) ||
            dfs(board, i + 1, j, s, k + 1) ||
            dfs(board, i - 1, j, s, k + 1)
        );
        //如果求出 flag 为 false,说明当前点是错的选择,不仅当前递归要返回false,还要把当前点恢复为未访问,让它后续能正常被访问。因为,基于当前路径,选当前点是不对的,但基于别的路径,走到这选它,有可能是对的。
        //如果求出 flag 为 true, 恢复与否都不影响。
        board[i][j] = s.charAt(k);
        return flag;
    }
}

未剪枝

class Solution {
    public boolean exist(char[][] board, String word) {
        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board[0].length; j++) {//每个格子都要判断
                if (dfs(board, i, j, word, 0)) {
                    return true;//只要从某个格子开始深搜能连城一串word,就说明能找到
                }
            }
        }
        return false;
    }
    //ij为格子坐标,k表示当前将要凑齐的word最大索引
    public boolean dfs(char[][] board, int i, int j, String s, int k) {
        int n = board.length, m = board[0].length;
        if (i > n - 1 || i < 0 || j > m - 1 || j < 0 || board[i][j] != s.charAt(k)) {
            return false;
        }
        if (k == s.length() - 1) {//找到了
            return true;
        }
        board[i][j] = '*';//防止重复访问,下一个节点的lrud可能覆盖到这个点
		
        //超时
        boolean l = dfs(board, i, j + 1, s, k + 1);
        boolean r = dfs(board, i, j - 1, s, k + 1);
        boolean u = dfs(board, i + 1, j, s, k + 1);
        boolean d = dfs(board, i - 1, j, s, k + 1);

        board[i][j] = s.charAt(k);
        return l || r || u || d;
    }
}

未剪枝2

class Solution {
    boolean flag = false;
    public boolean exist(char[][] board, String word) {
        int m = board.length, n = board[0].length;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                dfs(board, word, i, j, 0);
                if (flag) {
                    return true;
                }
            }
        }
        return false;
    }

    public void dfs(char[][] board, String word, int i, int j, int index) {
        if (i < 0 || j < 0 || i >= board.length || j >= board[0].length || word.charAt(index) != board[i][j]) {
            return;
        }
        if (index == word.length() - 1) {
            flag = true;
            return;
        }
        char tmp = board[i][j];
        board[i][j] = '@';
        dfs(board, word, i + 1, j, index + 1);
        dfs(board, word, i - 1, j, index + 1);
        dfs(board, word, i, j + 1, index + 1);
        dfs(board, word, i, j - 1, index + 1);
        board[i][j] = tmp;
    }
}