# 212. Word Search II

**<font color=red>难度: Hard</font>**

## 刷题内容

> 原题连接

* https://leetcode.com/problems/word-search-ii/

> 内容描述

```
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: 
words = ["oath","pea","eat","rain"] and board =
[
  ['o','a','a','n'],
  ['e','t','a','e'],
  ['i','h','k','r'],
  ['i','f','l','v']
]

Output: ["eat","oath"]
Note:
You may assume that all inputs are consist of lowercase letters a-z.
```

## 解题方案

> 思路 1
******- 时间复杂度: O(row * col * max_len(words))******- 空间复杂度: O(row * col)******


一看到就实现了一个DFS，超时了


```
class Solution(object):
    def findWords(self, board, words):
        """
        :type board: List[List[str]]
        :type words: List[str]
        :rtype: List[str]
        """
        if not board or not words:
            return []
        
        self.words = set(words)
        self.max_len_word = max(len(word) for word in self.words) 
        self.res = []
        self.row = len(board)
        self.col = len(board[0]) if self.row else 0
         
        for i in range(self.row):
            for j in range(self.col):
                self.dfs(board, board[i][j], i, j, set([(i,j)]))
                
        return self.res
            
    def dfs(self, board, path, x, y, visited):
        if path in self.words:
            self.res.append(path)
            self.words.remove(path)
            self.max_len_word = max(len(word) for word in self.words) if self.words else 0

        if len(path) >= self.max_len_word:
            return

        if x - 1 >= 0 and (x-1, y) not in visited:
            self.dfs(board, path+board[x-1][y], x-1, y, visited | set([(x-1, y)]))
        if y - 1 >= 0 and (x, y-1) not in visited:
            self.dfs(board, path+board[x][y-1], x, y-1, visited | set([(x, y-1)]))
        if x + 1 <= self.row - 1 and (x+1, y) not in visited:
            self.dfs(board, path+board[x+1][y], x+1, y, visited | set([(x+1, y)]))
        if y + 1 <= self.col - 1 and (x, y+1) not in visited:
            self.dfs(board, path+board[x][y+1], x, y+1, visited | set([(x, y+1)]))
```




> 思路 2
******- 时间复杂度: O(row * col * max_len(words))******- 空间复杂度: O(len(words) * word_len)******

看到这种字符串搜索，要想到利用trie树, beats 29.36%

```python
class TrieNode(object):

    def __init__(self):
        self.children = dict()
        self.isWordEnd = False

    def buildTrie(self, words):
        for word in words:
            curr = self
            for char in word:
                if char not in curr.children:
                    curr.children[char] = TrieNode()
                curr = curr.children[char]
            curr.isWordEnd = True
    
class Solution(object):
    def findWords(self, board, words):
        """
        :type board: List[List[str]]
        :type words: List[str]
        :rtype: List[str]
        """
        if not board or not words:
            return []
        
        root = TrieNode()
        root.buildTrie(words)
        self.res = set()
        self.row = len(board)
        self.col = len(board[0]) if self.row else 0
        
        def dfs(x, y, node, path):
            if node.isWordEnd:
                self.res.add(path)
            if x < 0 or x >= self.row or y < 0 or y >= self.col:
                return
            if board[x][y] in node.children:
                c = board[x][y]
                board[x][y] = '*' # backtracking
                dfs(x + 1, y, node.children[c], path + c)
                dfs(x - 1, y, node.children[c], path + c)
                dfs(x, y + 1, node.children[c], path + c)
                dfs(x, y - 1, node.children[c], path + c)
                board[x][y] = c
                
        for i in range(self.row):
            for j in range(self.col):
                dfs(i, j, root, '')
                
        return list(self.res)
```

> 思路 3
******- 时间复杂度: O(row * col * max_len(words))******- 空间复杂度: O(len(words) * word_len)******

看到一种更简单的trie实现模式，参考[yang.zheng.904](https://leetcode.com/problems/word-search-ii/discuss/59864/Python-code-use-trie-and-dfs-380ms)

beats 57.44%

```python
class Solution(object):
    def findWords(self, board, words):
        """
        :type board: List[List[str]]
        :type words: List[str]
        :rtype: List[str]
        """
        if not board or not words:
            return []
        
        # build trie 
        trie = {}
        for w in words:
            t = trie
            for c in w:
                if c not in t:
                    t[c] = {}
                t = t[c]
            t['#'] = '#'
            
        self.res = set()
        self.row = len(board)
        self.col = len(board[0]) if self.row else 0
        
        def dfs(x, y, node, path):
            if '#' in node:
                self.res.add(path)
            if x < 0 or x >= self.row or y < 0 or y >= self.col:
                return
            if board[x][y] in node:
                c = board[x][y]
                board[x][y] = '*' # backtracking
                dfs(x + 1, y, node[c], path + c)
                dfs(x - 1, y, node[c], path + c)
                dfs(x, y + 1, node[c], path + c)
                dfs(x, y - 1, node[c], path + c)
                board[x][y] = c
                
        for i in range(self.row):
            for j in range(self.col):
                dfs(i, j, trie, '')
                
        return list(self.res)
```

























