212. 單詞搜索 II
給定一個二維網格 board 和一個字典中的單詞列表 words,找出所有同時在二維網格和字典中出現的單詞。
單詞必須按照字母順序,通過相鄰的單元格內的字母構成,其中“相鄰”單元格是那些水平相鄰或垂直相鄰的單元格。同一個單元格內的字母在一個單詞中不允許被重複使用。
示例:
輸入:
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"]
說明:
你可以假設所有輸入都由小寫字母 a-z 組成。
提示:
你需要優化回溯算法以通過更大數據量的測試。你能否早點停止回溯?
如果當前單詞不存在於所有單詞的前綴中,則可以立即停止回溯。什麼樣的數據結構可以有效地執行這樣的操作?散列表是否可行?爲什麼? 前綴樹如何?如果你想學習如何實現一個基本的前綴樹,請先查看這個問題: 實現Trie(前綴樹)。
PS:
首先構建一個字典樹,然後在dfs的時候加入字典樹,以某個字符串結尾的可以減少搜索次數
這裏爲什麼不用開頭用結尾呢, (●ˇ∀ˇ●)
結尾在這裏就是我搜索完一個,我可以直接比較,如果沒有的話,我再返回上一個,
class TrieNode {
private static final int ALPHABET_SIZE = 26;
TrieNode[] children = new TrieNode[ALPHABET_SIZE];
// 判斷這個前綴是不是某個字符串的結尾
boolean isEndOfWord = false;
TrieNode() {
isEndOfWord = false;
for (int i = 0; i < ALPHABET_SIZE; i++)
children[i] = null;
}
}
class Trie {
public TrieNode root;
/** Initialize your data structure here. */
public Trie() {
root = new TrieNode();
}
/** Inserts a word into the trie. */
public void insert(String word) {
TrieNode curNode = root;
int index;
for (int i = 0; i < word.length(); i++) {
index = word.charAt(i) - 'a';
if (curNode.children[index] == null) {
curNode.children[index] = new TrieNode();
}
curNode = curNode.children[index];
}
curNode.isEndOfWord = true;
}
}
class Solution {
public List<String> findWords(char[][] board, String[] words) {
List<String> result = new ArrayList<>();
if (words == null || words.length == 0 || board == null || board.length == 0 || board[0].length == 0)
return result;
Trie trie = new Trie();
for (String temp : words)
trie.insert(temp);
TrieNode root = trie.root;
boolean[][] visited = new boolean[board.length][board[0].length];
Set<String> tempResult = new HashSet<>();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[0].length; j++) {
if (root.children[board[i][j] - 'a'] != null ) {
dfs(board, visited, i, j, root.children[board[i][j] - 'a'], tempResult, sb);
}
}
}
// 需要把tempResult這個set拷貝到真正的result List中進行返回
Iterator<String> iterator = tempResult.iterator();
while (iterator.hasNext()) {
result.add(iterator.next());
}
return result;
}
private void dfs(char[][] board, boolean[][] visited, int startIInBoard, int startJInBoard
, TrieNode curNode, Set<String> resultSet, StringBuilder curStrBuilder) {
curStrBuilder.append(board[startIInBoard][startJInBoard]);
visited[startIInBoard][startJInBoard] = true;
if (curNode.isEndOfWord) {
resultSet.add(curStrBuilder.toString());
}
// 向上搜索, 如果上面的格子沒有被搜索過的話
if (startIInBoard > 0 && !visited[startIInBoard - 1][startJInBoard]
&& curNode.children[board[startIInBoard - 1][startJInBoard] - 'a'] != null) {
dfs(board, visited,startIInBoard - 1, startJInBoard
, curNode.children[board[startIInBoard - 1][startJInBoard] - 'a'], resultSet, curStrBuilder);
}
// 向下搜索
if (startIInBoard < board.length - 1 && !visited[startIInBoard + 1][startJInBoard]
&& curNode.children[board[startIInBoard + 1][startJInBoard] - 'a'] != null) {
dfs(board, visited,startIInBoard + 1, startJInBoard
, curNode.children[board[startIInBoard + 1][startJInBoard] - 'a'], resultSet, curStrBuilder);
}
// 向左搜索
if (startJInBoard > 0 && !visited[startIInBoard][startJInBoard - 1]
&& curNode.children[board[startIInBoard][startJInBoard - 1] - 'a'] != null) {
dfs(board, visited, startIInBoard, startJInBoard - 1
, curNode.children[board[startIInBoard][startJInBoard - 1] - 'a'], resultSet, curStrBuilder);
}
// 向右搜索
if (startJInBoard < board[0].length - 1 && !visited[startIInBoard][startJInBoard + 1]
&& curNode.children[board[startIInBoard][startJInBoard + 1] - 'a'] != null) {
dfs(board, visited, startIInBoard, startJInBoard + 1
, curNode.children[board[startIInBoard][startJInBoard + 1] - 'a'], resultSet, curStrBuilder);
}
// 恢復現場
curStrBuilder.setLength(curStrBuilder.length() - 1);
visited[startIInBoard][startJInBoard] = false;
}
}