Problem:
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.
For example,
Given words = ["oath","pea","eat","rain"]
and board =
[ ['o','a','a','n'], ['e','t','a','e'], ['i','h','k','r'], ['i','f','l','v'] ]
Return ["eat","oath"]
.
Note:
You may assume that all inputs are consist of lowercase letters a-z
.
Analysis:
This problem is a very typical problem for using DFS. Search on a graph(matrix), find out the path to reach certain goal. At each vertix, we could follow all out edges. Key points: 1. Every gird on the matrix could be treated a start point. for (int i = 0; i < board.length; i++) { for (int j = 0; j < board[0].length; j++) { searchWord(board, i, j, "", visited, hash_set, ret); } } Before entering the recursive call, we only specify the index(i, j). And only at the recursive funtion, we really add the character at grid(i, j) into our temporary path. This design could have following benefits: 1. When we fork out search branch(left, right, above, below), we don't need to check if the index(i, j) is valid, which could make the code quite concise. ------------------------------------------------------------ searchWord(board, i-1, j, cur_str, visited, hash_set, ret); searchWord(board, i+1, j, cur_str, visited, hash_set, ret); searchWord(board, i, j-1, cur_str, visited, hash_set, ret); searchWord(board, i, j+1, cur_str, visited, hash_set, ret); ----------------------------------------------------------- To avoid invalid index, we could simplily do it at the beginning of the recursive funtion, when the index was just passed in. if (i < 0 || j < 0 || i >= board.length || j >= board[0].length || visited[i][j]) return; 2. It is easy to recover changed state(When back tracking). Let us suppose you change the state before enter the searchWord. for (int i = 0; i < board.length; i++) { for (int j = 0; j < board[0].length; j++) { visited[i][j] = true; searchWord(board, i, j, "", visited, hash_set, ret); visited[i][j] = true; } } To make all changed states could be recovered, you have to do above recover thing for all forked branch. visited[i-1][j] = true; searchWord(board, i-1, j, cur_str, visited, hash_set, ret); visited[i-1][j] = false; visited[i+1][j] = true; searchWord(board, i+1, j, cur_str, visited, hash_set, ret); visited[i+1][j] = false; visited[i][j-1] = true; searchWord(board, i, j-1, cur_str, visited, hash_set, ret); visited[i][j-1] = false; visited[i][j+1] = true; searchWord(board, i, j+1, cur_str, visited, hash_set, ret); visited[i][j+1] = false; How ugly? Right! What'more!!! You need to check is visisted[i][j] is valid for every forked branch. Sky: Since we do the same recover work for all grid on the matrix, we do not do it together at recursive call? 3. For this DFS problem, each grid could go four directions. To avoid the path back to visited grid, thus result in infinite loop, we should record all gird we have visited. if (i < 0 || j < 0 || i >= board.length || j >= board[0].length || visited[i][j]) return; visited[i][j] = true; check and fork ... visited[i][j] = false; Since for each grid, we actually search the whole matrix, the search cost is O(m*n). There are m*n grids in total, Thus the overall time complexity is O(m^2*n^2).
Solution:
public class Solution { public List<String> findWords(char[][] board, String[] words) { if (board == null || words == null) throw new IllegalArgumentException("Plese check the arguments passed into the funtion"); ArrayList<String> ret = new ArrayList<String> (); if (board.length == 0) return ret; HashSet<String> hash_set = new HashSet<String> (); boolean[][] visited = new boolean[board.length][board[0].length]; for (int i = 0; i < words.length; i++) hash_set.add(words[i]); for (int i = 0; i < board.length; i++) { for (int j = 0; j < board[0].length; j++) { searchWord(board, i, j, "", visited, hash_set, ret); } } return ret; } private void searchWord(char[][] board, int i, int j, String cur_str, boolean[][] visited, HashSet<String> hash_set, ArrayList<String> ret) { if (i < 0 || j < 0 || i >= board.length || j >= board[0].length || visited[i][j]) return; visited[i][j] = true; cur_str = cur_str + board[i][j]; if (hash_set.contains(cur_str)) ret.add(cur_str); searchWord(board, i-1, j, cur_str, visited, hash_set, ret); searchWord(board, i+1, j, cur_str, visited, hash_set, ret); searchWord(board, i, j-1, cur_str, visited, hash_set, ret); searchWord(board, i, j+1, cur_str, visited, hash_set, ret); visited[i][j] = false; } }