Welcome to Subscribe On Youtube

Question

Formatted question description: https://leetcode.ca/all/212.html

Given an m x n board of characters and a list of strings words, return all words on the board.

Each word must be constructed from letters of sequentially adjacent cells, where adjacent cells are horizontally or vertically neighboring. The same letter cell may not be used more than once in a word.

 

Example 1:

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"]

Example 2:

Input: board = [["a","b"],["c","d"]], words = ["abcb"]
Output: []

 

Constraints:

  • m == board.length
  • n == board[i].length
  • 1 <= m, n <= 12
  • board[i][j] is a lowercase English letter.
  • 1 <= words.length <= 3 * 104
  • 1 <= words[i].length <= 10
  • words[i] consists of lowercase English letters.
  • All the strings of words are unique.

Algorithm

The solution to problem 79. Word Search can be reused here. Just search for each word in words and check whether it exists, and add the words that exist to the result list.

There also exist other solutions like Trie + DFS.

Code

Trie

  • import java.util.ArrayList;
    import java.util.List;
    
    public class Word_Search_II {
    
        public static void main(String[] args) {
            Word_Search_II out = new Word_Search_II();
            Solution s = out.new Solution();
    
            System.out.println(
                s.findWords(
                    new char[][]{
                        {'o','a','a','n'},
                        {'e','t','a','e'},
                        {'i','h','k','r'},
                        {'i','f','l','v'} },
                    new String[]{"oath","pea","eat","rain"}
            ));
    
            System.out.println(
                s.findWords(
                    new char[][]{
                        {'a', 'b'},
                        {'a', 'a'} },
                    new String[]{"aba","baa","bab","aaab","aaa","aaaa","aaba"}
                )); // output: ["aaa","aaab","aaba","aba","baa"]
        }
    
    
        /*
    
    test case: huge data
    
    board:
        [["a","a","a","a","a","a","a","a","a","a","a","a"],["a","a","a","a","a","a","a","a","a","a","a","a"],["a","a","a","a","a","a","a","a","a","a","a","a"],["a","a","a","a","a","a","a","a","a","a","a","a"],["a","a","a","a","a","a","a","a","a","a","a","a"],["a","a","a","a","a","a","a","a","a","a","a","a"],["a","a","a","a","a","a","a","a","a","a","a","a"],["a","a","a","a","a","a","a","a","a","a","a","a"],["a","a","a","a","a","a","a","a","a","a","a","a"],["a","a","a","a","a","a","a","a","a","a","a","a"],["a","a","a","a","a","a","a","a","a","a","a","a"],["a","a","a","a","a","a","a","a","a","a","a","a"]]
    
    words:
        ["a","aa","aaa","aaaa","aaaaa","aaaaaa","aaaaaaa","aaaaaaaa","aaaaaaaaa","aaaaaaaaaa"]
    
         */
    
        class TrieNode {
            public TrieNode[] children;
            public String word;
    
            public TrieNode() {
                children = new TrieNode[26];
                word = null;
                for (int i = 0; i < 26; i++) {
                    children[i] = null;
                }
            }
        }
    
        class Trie {
            TrieNode root;
            /** Initialize your data structure here. */
            public Trie() {
                root = new TrieNode();
            }
    
            /** Inserts a word into the trie. */
            public void insert(String word) {
                char[] array = word.toCharArray();
                TrieNode cur = root;
                for (int i = 0; i < array.length; i++) {
                    if (cur.children[array[i] - 'a'] == null) {
                        cur.children[array[i] - 'a'] = new TrieNode();
                    }
                    cur = cur.children[array[i] - 'a'];
                }
                cur.word = word;
            }
        };
    
        class Solution {
            public List<String> findWords(char[][] board, String[] words) {
                Trie trie = new Trie();
    
                List<String> res = new ArrayList<>();
                for (String word : words) {
                    trie.insert(word);
                }
                int rows = board.length;
                if (rows == 0) {
                    return res;
                }
                int cols = board[0].length;
    
                for (int i = 0; i < rows; i++) {
                    for (int j = 0; j < cols; j++) {
                        dfs(board, i, j, trie.root, res);
                    }
                }
                return res;
            }
    
            private void dfs(char[][] board, int row, int col, TrieNode node, List<String> res) {
                if (row < 0 || row >= board.length || col < 0 || col >= board[0].length) {
                    return;
                }
                char cur = board[row][col];
                
                if (cur == '$' || node.children[cur - 'a'] == null) {
                    return;
                }
                node = node.children[cur - 'a'];
    
                if (node.word != null) {
                    res.add(node.word);
                    node.word = null;
                }
                char temp = board[row][col];
    
                board[row][col] = '$';
                dfs(board, row - 1, col, node, res);
                dfs(board, row + 1, col, node, res);
                dfs(board, row, col - 1, node, res);
                dfs(board, row, col + 1, node, res);
                board[row][col] = temp;
            }
        }
    }
    
    ############
    
    class Trie {
        Trie[] children = new Trie[26];
        String w;
    
        void insert(String w) {
            Trie node = this;
            for (char c : w.toCharArray()) {
                c -= 'a';
                if (node.children[c] == null) {
                    node.children[c] = new Trie();
                }
                node = node.children[c];
            }
            node.w = w;
        }
    }
    
    class Solution {
        private Set<String> ans = new HashSet<>();
        private int m;
        private int n;
        private char[][] board;
    
        public List<String> findWords(char[][] board, String[] words) {
            Trie trie = new Trie();
            for (String w : words) {
                trie.insert(w);
            }
            m = board.length;
            n = board[0].length;
            this.board = board;
            for (int i = 0; i < m; ++i) {
                for (int j = 0; j < n; ++j) {
                    dfs(trie, i, j);
                }
            }
            return new ArrayList<>(ans);
        }
    
        private void dfs(Trie node, int i, int j) {
            int idx = board[i][j] - 'a';
            if (node.children[idx] == null) {
                return;
            }
            node = node.children[idx];
            if (node.w != null) {
                ans.add(node.w);
            }
            char c = board[i][j];
            board[i][j] = '0';
            int[] dirs = {-1, 0, 1, 0, -1};
            for (int k = 0; k < 4; ++k) {
                int x = i + dirs[k], y = j + dirs[k + 1];
                if (x >= 0 && x < m && y >= 0 && y < n && board[x][y] != '0') {
                    dfs(node, x, y);
                }
            }
            board[i][j] = c;
        }
    }
    
  • // OJ: https://leetcode.com/problems/word-search-ii/
    // Time: O(WL + MN * 4^L) where M, N is the size of board, W is the size of words and L is the average length of word
    // Space: O(WL)
    struct TrieNode {
        TrieNode *next[26] = {0};
        bool word = false;
    };
    void addWord(TrieNode *node, string &w) {
        for (char c : w) {
            if (node->next[c - 'a'] == NULL) node->next[c - 'a'] = new TrieNode();
            node = node->next[c - 'a'];
        }
        node->word = true;
    }
    class Solution {
        int M, N, dirs[4][2] = { {0,1}, {0,-1}, {1,0}, {-1,0} };
        vector<string> ans;
        string path;
        void dfs(vector<vector<char>> &A, TrieNode *node, int x, int y) {
            char c = A[x][y];
            node = node->next[c - 'a'];
            if (!node) return;
            path.push_back(c);
            A[x][y] = 0;
            if (node->word) {
                ans.push_back(path);
                node->word = false; // prevent adding the same word again.
            }
            for (auto &dir : dirs) {
                int a = x + dir[0], b = y + dir[1];
                if (a >= M || a < 0 || b >= N || b < 0 || A[a][b] == 0) continue;
                dfs(A, node, a, b);
            }
            A[x][y] = c;
            path.pop_back();
        }
    public:
        vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {
            if (board.empty() || board[0].empty()) return {};
            M = board.size(), N = board[0].size();
            TrieNode root;
            for (auto &w : words) addWord(&root, w);
            for (int i = 0; i < M; ++i) {
                for (int j = 0; j < N; ++j) dfs(board, &root, i, j);
            }
            return ans;
        }
    };
    
  • class Trie:
        def __init__(self):
            self.children = [None] * 26
            self.w = ''
            # minor ajust, not a boolean is_end, but the whole word
            # so it's easier for dfs to save the word
    
        def insert(self, w):
            node = self
            for c in w:
                idx = ord(c) - ord('a')
                if node.children[idx] is None:
                    node.children[idx] = Trie()
                node = node.children[idx]
            node.w = w
    
    
    class Solution:
        def findWords(self, board: List[List[str]], words: List[str]) -> List[str]:
            def dfs(node, i, j):
                idx = ord(board[i][j]) - ord('a')
                if node.children[idx] is None:
                    return
                node = node.children[idx]
                if node.w:
                    ans.add(node.w)
                c = board[i][j]
                board[i][j] = '0'
                for a, b in [[0, -1], [0, 1], [1, 0], [-1, 0]]:
                    x, y = i + a, j + b
                    if 0 <= x < m and 0 <= y < n and board[x][y] != '0':
                        dfs(node, x, y)
                board[i][y] = c
    
            trie = Trie()
            for w in words:
                trie.insert(w)
            ans = set()
            m, n = len(board), len(board[0])
            for i in range(m):
                for j in range(n):
                    dfs(trie, i, j)
            return list(ans)
    
    
    ################
    
    class TrieNode:
      def __init__(self, char):
        self.neighbours = {}
        self.isWord = False
    
    
    class Trie:
      def __init__(self):
        self.root = TrieNode("-")
    
      def addWord(self, word):
        root = self.root
        for i in range(0, len(word)):
          c = word[i]
          if c in root.neighbours:
            root = root.neighbours[c]
          else:
            newnode = TrieNode(c)
            root.neighbours[c] = newnode
            root = root.neighbours[c]
        root.isWord = True
    
    
    class Solution:
      # @param board, a list of lists of 1 length string
      # @param words: A list of string
      # @return: A list of string
      def findWords(self, board, words):
        # write your code here
        trie = Trie()
        res = []
        visited = [[0] * len(board[0]) for i in range(0, len(board))]
        directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
    
        def dfs(i, j, board, visited, res, root, path):
          if not root:
            return
    
          if root.isWord:
            res.append(path)
    
          for direction in directions:
            ni, nj = i + direction[0], j + direction[1]
            if 0 <= ni < len(board) and 0 <= nj < len(board[0]):
              c = board[ni][nj]
              if visited[ni][nj] == 0:
                visited[ni][nj] = 1
                dfs(ni, nj, board, visited, res, root.neighbours.get(c, None), path + c)
                visited[ni][nj] = 0
    
        for word in words:
          trie.addWord(word)
        root = trie.root
        for i in range(0, len(board)):
          for j in range(0, len(board[0])):
            c = board[i][j]
            visited[i][j] = 1
            dfs(i, j, board, visited, res, root.neighbours.get(c, None), c)
            visited[i][j] = 0
        return list(set(res))
    
    
  • type Trie struct {
    	children [26]*Trie
    	w        string
    }
    
    func newTrie() *Trie {
    	return &Trie{}
    }
    func (this *Trie) insert(word string) {
    	node := this
    	for _, c := range word {
    		c -= 'a'
    		if node.children[c] == nil {
    			node.children[c] = newTrie()
    		}
    		node = node.children[c]
    	}
    	node.w = word
    }
    
    func findWords(board [][]byte, words []string) []string {
    	trie := newTrie()
    	for _, w := range words {
    		trie.insert(w)
    	}
    	m, n := len(board), len(board[0])
    	res := map[string]bool{}
    	var dfs func(node *Trie, i, j int)
    	dfs = func(node *Trie, i, j int) {
    		idx := board[i][j] - 'a'
    		if node.children[idx] == nil {
    			return
    		}
    		node = node.children[idx]
    		if node.w != "" {
    			res[node.w] = true
    		}
    		dirs := []int{-1, 0, 1, 0, -1}
    		c := board[i][j]
    		board[i][j] = '0'
    		for k := 0; k < 4; k++ {
    			x, y := i+dirs[k], j+dirs[k+1]
    			if x >= 0 && x < m && y >= 0 && y < n && board[x][y] != '0' {
    				dfs(node, x, y)
    			}
    		}
    		board[i][j] = c
    	}
    	for i, row := range board {
    		for j := range row {
    			dfs(trie, i, j)
    		}
    	}
    	var ans []string
    	for v := range res {
    		ans = append(ans, v)
    	}
    	return ans
    }
    
  • class Trie {
        children: Trie[];
        ref: number;
    
        constructor() {
            this.children = new Array(26);
            this.ref = -1;
        }
    
        insert(w: string, ref: number): void {
            let node: Trie = this;
            for (let i = 0; i < w.length; i++) {
                const c = w.charCodeAt(i) - 97;
                if (node.children[c] == null) {
                    node.children[c] = new Trie();
                }
                node = node.children[c];
            }
            node.ref = ref;
        }
    }
    
    function findWords(board: string[][], words: string[]): string[] {
        const tree = new Trie();
        for (let i = 0; i < words.length; ++i) {
            tree.insert(words[i], i);
        }
        const m = board.length;
        const n = board[0].length;
        const ans: string[] = [];
        const dirs: number[] = [-1, 0, 1, 0, -1];
        const dfs = (node: Trie, i: number, j: number) => {
            const idx = board[i][j].charCodeAt(0) - 97;
            if (node.children[idx] == null) {
                return;
            }
            node = node.children[idx];
            if (node.ref != -1) {
                ans.push(words[node.ref]);
                node.ref = -1;
            }
            const c = board[i][j];
            board[i][j] = '#';
            for (let k = 0; k < 4; ++k) {
                const x = i + dirs[k];
                const y = j + dirs[k + 1];
                if (x >= 0 && x < m && y >= 0 && y < n && board[x][y] != '#') {
                    dfs(node, x, y);
                }
            }
            board[i][j] = c;
        };
        for (let i = 0; i < m; ++i) {
            for (let j = 0; j < n; ++j) {
                dfs(tree, i, j);
            }
        }
        return ans;
    }
    
    

All Problems

All Solutions