给定一个二维网格 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
组成。
解题思路
使用字典树来进行查询,提高查询效率。把String[] words 全部加入字典树,用一个boolean二维数组visited来记录是否被访问过的数据。在DFS遍历时,判断i、j是否越界,判断之前是否被访问过、判断前缀是否在字典树内 作为递归终止条件。
0、使用Set来去掉重复结果集
1、把String[] words 全部加入到字典树
2、遍历board二维数组,进行DFS。
3、编写DFS,dfs(char[][] board, Trie trie, boolean[][] visited, String str(每一组的字符), int x(下标), int y(下标)){
递归返回:判断i、j是否越界,判断之前是否被访问过、判断前缀是否在字典树内
加入结果集,把visited状态标志修改,然后递归,上下左右,然后回溯,修改visited的标志
class Solution {
public HashSet<String> result = new HashSet<>();//1,定义结果set,去掉重复结果集,
//例如Input
//[["a","a"]]
//["a"]
//Output
//["a","a"]
//Expected
//["a"]
public List<String> findWords(char[][] board, String[] words) {
//2,使用Tire,添加入trie
Trie trie = new Trie();
for(String word : words){
trie.insert(word);
}
//分别获得board的一二维长度
int m = board.length;//获得行的长度
int n = board[0].length;//获得每一行中的个数,即列数
//定义boolean数组,存储是否被访问过的数据
boolean[][] visited = new boolean[m][n];
//双重循环遍历board矩阵
for(int i = 0; i < m; i++){
for(int j = 0; j < n ; j++){
//DFS
dfs(board,trie,visited,"",i,j);
}
}
return new ArrayList<String>(result);
}
public void dfs(char[][] board, Trie trie, boolean[][] visited, String str, int x, int y){
//判断i,j是否越界
if(x < 0 || x >= board.length || y < 0 || y >= board[0].length)
return;
//判断之前是否被访问过,如果被访问过,就返回,无需再遍历
if(visited[x][y])
return;
//把char字符加入字符串
str += board[x][y];
//如果前缀都不是,也直接返回
if(!trie.startsWith(str))
return;
//在tried里面查询是否有str,如果有,则添加
if(trie.search(str)){
result.add(str);
}
//把visited的状态改成true
visited[x][y] = true;
//DFS
dfs(board, trie, visited, str, x - 1, y);//上
dfs(board, trie, visited, str, x + 1, y);//下
dfs(board, trie, visited, str ,x, y - 1);//左
dfs(board, trie, visited, str, x, y + 1);//右
//回溯
visited[x][y] = false;
}
}
class Trie {
private TrieNode root;//定义根节点
/** Initialize your data structure here. */
public Trie() {
root = new TrieNode();
root.val = ' ';//根节点为空
}
/** Inserts a word into the trie. */
public void insert(String word) {
TrieNode ws = root;//root赋值给新变量
//遍历全部的word
for(int i = 0; i < word.length(); i++){
//获取word中的单个字符
char c = word.charAt(i);//获取字符
if(ws.children[c - 'a'] == null){//判断该字符是否存在于当前的节点的子节点中
ws.children[c - 'a'] = new TrieNode(c);//赋予节点对象
}
ws = ws.children[c - 'a'];//下一个节点为ws的子节点
}
ws.isWord = true;
}
/** Returns if the word is in the trie. */
public boolean search(String word) {
TrieNode ws = root;
for(int i = 0; i < word.length();i++){
char c = word.charAt(i);
if(ws.children[c - 'a'] == null){
return false;
}
ws = ws.children[c - 'a'];
}
return ws.isWord;
}
/** Returns if there is any word in the trie that starts with the given prefix. */
public boolean startsWith(String prefix) {
TrieNode ws = root;
for(int i = 0; i < prefix.length();i++){
char c = prefix.charAt(i);
if(ws.children[c - 'a'] == null)
return false;
ws = ws.children[c - 'a'];
}
return true;
}
}
class TrieNode{//定义节点类
public char val;
public boolean isWord;//是否一个单词的最后的节点
public TrieNode[] children = new TrieNode[26];//26个小写的孩子节点
public TrieNode(){}
public TrieNode(char c){
isWord = false;
this.val = c;
}
}