LeetCode Top Interview Questions 212. Word Search II (Java版; Hard)

welcome to my blog

LeetCode Top Interview Questions 212. Word Search II (Java版; Hard)

题目描述
Given a 2D board and a list of words from the dictionary, find all words in the board.

Each word must 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 in a word.

 

Example:

Input: 
board = [
  ['o','a','a','n'],
  ['e','t','a','e'],
  ['i','h','k','r'],
  ['i','f','l','v']
]
words = ["oath","pea","eat","rain"]

Output: ["eat","oath"]
第一次做; 前缀树+回溯(DFS); 要清晰的认识到, 回溯法就是DFS深度优先遍历的一种具体实现方法
/*
为什么要用前缀树?这么想, 如果words中只有一个单词, 那么只需要在board的各个位置进行dfs,看看能不能搜索出这个单词即可;
如果words中有多个单词,比如是a,b,c, 那么处理单词a时,需要在board的各个位置进行dfs; 处理单词b时需要在board的各个位置进行dfs; 处理单词c时也一样; 复杂度过高
所以要用前缀树, 实际上是拿空间换时间, 使用前缀树的话, 只需要在board的各个位置进行dfs, 寻找前缀树的特殊节点, 特殊节点是某个单词的最后一个字符
*/
class Solution {
    public List<String> findWords(char[][] board, String[] words) {
        List<String> res = new ArrayList<>();
        if(board==null||board.length==0||board[0]==null||board[0].length==0)
            return res;
        //创建前缀树
        TrieNode root = buildTrie(words);
        for(int i=0; i<board.length; i++){
            for(int j=0; j<board[0].length; j++){
                dfs(board,i,j,root,res);
            }
        }
        return res;
    }
    //深度优先遍历:回溯法; 核心: 不需要words作为参数了, 因为已经把words的信息全都整合到前缀树中了
    public void dfs(char[][] board, int i, int j, TrieNode root, List<String> res){
        //base case
        char ch = board[i][j];
        //细节:数组索引得是整数, 别无脑地写成ch
        if(ch=='#' || root.next[ch-'a']==null)
            return;
        //
        //改变现场
        board[i][j] = '#';
        root = root.next[ch-'a'];
        //检查是否找到words中的单词
        //如果ch是某个单词的最后一个字符的话, 说明找到了一个words中的单词, 加入到res中
        if(root.word!=null){
            res.add(root.word);
            //核心:为了防止重复加入word, 将root.root置为null
            root.word=null;
        }
            
        //新条件新递归
        if(i-1>=0)
            dfs(board, i-1, j, root, res);
        if(i+1<board.length)
            dfs(board, i+1, j, root, res);
        if(j-1>=0)
            dfs(board, i, j-1, root, res);
        if(j+1<board[0].length)
            dfs(board, i, j+1, root, res);
        //恢复现场
        board[i][j] = ch;
        
    }
    //创建前缀树, 根节点是个空节点; 核心: 一切操作都从根节点开始, 所以要返回根节点
    public TrieNode buildTrie(String[] words){
        //上来先创建根节点
        TrieNode root = new TrieNode();
        //以root为根,建立前缀树
        for(String w : words){
            //从root开始
            TrieNode p = root;
            for(char ch : w.toCharArray()){
                //ch对应的整数索引
                int i = ch - 'a';
                //没有从p到i的路的话, 则创建i节点; 有从p到i的路的话就不用创建i节点了
                if(p.next[i]==null)
                    p.next[i] = new TrieNode();
                //update; p来到i节点
                p = p.next[i];
            }
            //此时p指向word最有一个字符, 以该字符结尾的路径代表的单词是word, 记录下来
            p.word=w;
        }
        return root;
    }
    class TrieNode{
        //26表示26个英文字母
        TrieNode[] next = new TrieNode[26];
        //word表示以当前TrieNode结尾的单词是word; 跟之前的end有类似的感觉
        String word;
    }
}
LeetCode最优解
public List<String> findWords(char[][] board, String[] words) {
    List<String> res = new ArrayList<>();
    TrieNode root = buildTrie(words);
    for (int i = 0; i < board.length; i++) {
        for (int j = 0; j < board[0].length; j++) {
            dfs (board, i, j, root, res);
        }
    }
    return res;
}

public void dfs(char[][] board, int i, int j, TrieNode p, List<String> res) {
    char c = board[i][j];
    if (c == '#' || p.next[c - 'a'] == null) return;
    p = p.next[c - 'a'];
    if (p.word != null) {   // found one
        res.add(p.word);
        p.word = null;     // de-duplicate
    }

    board[i][j] = '#';
    if (i > 0) dfs(board, i - 1, j ,p, res); 
    if (j > 0) dfs(board, i, j - 1, p, res);
    if (i < board.length - 1) dfs(board, i + 1, j, p, res); 
    if (j < board[0].length - 1) dfs(board, i, j + 1, p, res); 
    board[i][j] = c;
}

public TrieNode buildTrie(String[] words) {
    TrieNode root = new TrieNode();
    for (String w : words) {
        TrieNode p = root;
        for (char c : w.toCharArray()) {
            int i = c - 'a';
            if (p.next[i] == null) p.next[i] = new TrieNode();
            p = p.next[i];
       }
       p.word = w;
    }
    return root;
}

class TrieNode {
    TrieNode[] next = new TrieNode[26];
    String word;
}

你可能感兴趣的:(LeetCode,java)