【LeetCode】212. Word Search II

1. Title

Given a two-dimensional grid board and a list of words in a dictionary , find all the words that appear in the two-dimensional grid and the dictionary at the same time.

Words must be in alphabetical order and formed by letters in adjacent cells, where "adjacent" cells are those that are adjacent horizontally or vertically. Letters in the same cell are not allowed to be used repeatedly in a word.

Example:

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

输出: ["eat","oath"]

Explanation:
You can assume that all input consists of lowercase letters az.

prompt:

  • You need to optimize the backtracking algorithm to pass a larger amount of data test. Can you stop backtracking sooner?
  • If the current word does not exist in the prefix of all words, you can stop backtracking immediately. What kind of data structure can efficiently perform such operations? Is a hash table feasible? why? How about the prefix tree? If you want to learn how to implement a basic prefix tree, please check this question first: Implement Trie (prefix tree).

Two, solve

1. Violent DFS

Ideas:

Traverse the two-dimensional array and perform DFS on each character. If the result of the traversal is in the given words list, the word is added to the returned result.

Code:

Here is omitted, for details, you can view the detailed analysis of popular ideas and multiple solutions .

Time complexity: O (M ∗ (4 ∗ 3 L − 1) O(M*(4*3^{L-1})O ( M(43L 1 ), M is the number of cells, and L is the maximum length of a word.
Space complexity: O (N) O(N)O ( N ) , the total number of letters in the dictionary.

2、Trie+DFS

version 1

Ideas:

1.1 Trie tree

For Trie's data structure and common operation implementation, please see [LeetCode] 208. Implement Trie (prefix tree)

1.2 DFS template

voidDFS ( Vertex V ) 
{
    
    
	visited[V] = true;  
	for( V 的每个邻接点W )
		if( !visited[W] )
	DFS( W );
}

1.3 Process

First traverse the words to form the corresponding Trie tree, and then traverse the given two-dimensional character array board, traversing line by line. For each character, determine whether it is in the Trie tree. If it is, then perform DFS around it, and add the return result ; If not, skip, go to the next character, and continue DFS until the end of the board traversal.

Code:

The code for this question is written on the basis of [LeetCode] 208. Trie (prefix tree) code.

class TrieNode {
    
    
    boolean isEnd;
    Map<Character, TrieNode> next = new HashMap<>();
}

class Trie {
    
    

    TrieNode root = new TrieNode();

    public Trie() {
    
    }

    public void insert(String word) {
    
    
        TrieNode curr = root;
        for (char c : word.toCharArray()) {
    
    
            if (!curr.next.containsKey(c)) {
    
    
                TrieNode tmp = new TrieNode();
                curr.next.put(c, tmp);
            }
            curr = curr.next.get(c);
        }
        curr.isEnd = true;
    }

    public boolean search(String word) {
    
    
        TrieNode curr = root;
        for (char c : word.toCharArray()) {
    
    
            if (!curr.next.containsKey(c)) return false;
            curr = curr.next.get(c);
        }
        return curr.isEnd;
    }

    public boolean startsWith(String prefix) {
    
    
        TrieNode curr = root;
        for (char c : prefix.toCharArray()) {
    
    
            if (!curr.next.containsKey(c)) return false;
            curr = curr.next.get(c);
        }
        return true;
    }
}


class Solution {
    
    
    
    private Set<String> res = new HashSet<>();
    private int rows, cols;

    public List<String> findWords(char[][] board, String[] words) {
    
    
        
        Trie trie = new Trie();
        for (String word : words) {
    
    
            trie.insert(word);
        }        

        rows = board.length; cols = board[0].length;
        boolean[][] visited = new boolean[rows][cols];
        for (int row=0; row<rows; row++) {
    
    
            for (int col=0; col<cols; col++) {
    
    
                DFS(board, trie, visited, "", row, col);
            }
        }
        return new ArrayList<String>(res);
    }

    public void DFS(char[][] board, Trie trie, boolean[][] visited, String str, int row, int col) {
    
    
        
        if (row<0 || row>=rows || col<0 || col>=cols) return;
        if (visited[row][col]) return;

        str += board[row][col];
        if (!trie.startsWith(str)) return;
        if (trie.search(str)) {
    
    
            res.add(str);
        }
        
        visited[row][col] = true;
        DFS(board, trie, visited, str, row+1, col);
        DFS(board, trie, visited, str, row-1, col);
        DFS(board, trie, visited, str, row, col+1);
        DFS(board, trie, visited, str, row, col-1);
        visited[row][col] = false;
    }
}

Time complexity: O (M ∗ (4 ∗ 3 L − 1) O(M*(4*3^{L-1})O ( M(43L 1 ), M is the number of cells, and L is the maximum length of a word.
Space complexity: O (N) O(N)O ( N ) , the total number of letters in the dictionary.

Version 2

Idea: Same as version 1.

Code:

Slightly simplify version 1, as follows:

class Solution {
    
    
    // Trie Node
    class TrieNode {
    
    
        TrieNode[] children = new TrieNode[26];
        String word;
    }

    // build the trie from words array
    public TrieNode buildTrie(String[] words) {
    
    
        TrieNode root = new TrieNode();
        for (String w : words) {
    
    
            TrieNode current = root;
            for (char c : w.toCharArray()) {
    
    
                int i = c - 'a'; // index adjustment
                if (current.children[i] == null) 
					current.children[i] = new TrieNode();
                current = current.children[i];
            }
            current.word = w; // assign word w to word of trie node
        }
		
        return root;
    }


    public List<String> findWords(char[][] board, String[] words) {
    
    
        List<String> result = new ArrayList<>();
        // build the trie from using dictionary words.
        TrieNode root = buildTrie(words);

        // call the dfs
        for (int i = 0; i < board.length; i++) {
    
    
            for (int j = 0; j < board[0].length; j++) {
    
    
                dfs(board, i, j, root, result);
            }
        }

        return result;
    }

    public void dfs(char[][] board, int i, int j, TrieNode root, List<String> result) {
    
    
        char c = board[i][j]; // get the current character from the board at i, j
        if (c == '*' || root.children[c - 'a'] == null)
            return;
        root = root.children[c - 'a'];
        if (root.word != null) {
    
       // found one words add in the result list
            result.add(root.word);
            root.word = null;     // de-duplicate remove the word from trie
        }

        board[i][j] = '*'; // update the character of at i , j no need for visited array
        if (i > 0) dfs(board, i - 1, j, root, result); // up
        if (j > 0) dfs(board, i, j - 1, root, result); // left
        if (i < board.length - 1) dfs(board, i + 1, j, root, result); // down
        if (j < board[0].length - 1) dfs(board, i, j + 1, root, result); // right
        board[i][j] = c; // backtrack the character
    }
}

Time complexity: O (M ∗ (4 ∗ 3 L − 1) O(M*(4*3^{L-1})O ( M(43L 1 ), M is the number of cells, and L is the maximum length of a word.
Space complexity: O (N) O(N)O ( N ) , the total number of letters in the dictionary.

Three, reference

1. Java 15ms Easiest Solution (100.00%)
2. Two Solution | Trie DFS Backtracking | Steps & Well commented
3. My simple and clean Java code using DFS and Trie
4. Word Search II
5. Detailed and popular thinking analysis, multiple solutions

Guess you like

Origin blog.csdn.net/HeavenDan/article/details/108683039