一、題目描述
給定一個二維網格 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(前綴樹)。
二、解題思路
這是一道hard題,原因是考察了:字典樹 和 回溯法+動態規劃。
具體思路就是先實現字典樹的數據結構,再用回溯求出結果。
三、可運行java代碼
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
public class findWords_212 {
public List<String> findWords(char[][] board, String[] words) {
//構建字典樹
wordTrie myTrie=new wordTrie();
trieNode root=myTrie.root;
for(String s:words)
myTrie.insert(s);
//使用set防止重複
Set<String> result =new HashSet<>();
int m=board.length;
int n=board[0].length;
boolean [][]visited=new boolean[m][n];
//遍歷整個二維數組
for(int i=0;i<board.length; i++){
for (int j = 0; j < board [0].length; j++){
find(board,visited,i,j,m,n,result,root);
}
}
System.out.print(result);
return new LinkedList<String>(result);
}
private void find(char [] [] board, boolean [][]visited,int i,int j,int m,int n,Set<String> result,trieNode cur){
//邊界以及是否已經訪問判斷
if(i<0||i>=m||j<0||j>=n||visited[i][j])
return;
cur=cur.child[board[i][j]-'a'];
visited[i][j]=true;
if(cur==null)
{
//如果單詞不匹配,回退
visited[i][j]=false;
return;
}
//找到單詞加入
if(cur.isLeaf)
{
result.add(cur.val);
//找到單詞後不能回退,因爲可能是“ad” “addd”這樣的單詞得繼續回溯
// visited[i][j]=false;
// return;
}
find(board,visited,i+1,j,m,n,result,cur);
find(board,visited,i,j+1,m,n,result,cur);
find(board,visited,i,j-1,m,n,result,cur);
find(board,visited,i-1,j,m,n,result,cur);
//最後要回退,因爲下一個起點可能會用到上一個起點的字符
visited[i][j]=false;
}
}
//字典樹
class wordTrie{
public trieNode root=new trieNode();
public void insert(String s){
trieNode cur=root;
for(char c:s.toCharArray()){
if(cur.child[c-'a']==null){
cur.child [c-'a'] = new trieNode();
cur=cur.child[c-'a'];
}else
cur=cur.child [c-'a'];
}
cur.isLeaf=true;
cur.val=s;
}
}
//字典樹結點
class trieNode{
public String val;
public trieNode[] child=new trieNode[26];
public boolean isLeaf=false;
trieNode(){
}
}