212. 单词搜索 II
难度困难728
给定一个 m x n
二维字符网格 board
和一个单词(字符串)列表 words
, 返回所有二维网格上的单词 。
单词必须按照字母顺序,通过 相邻的单元格 内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母在一个单词中不允许被重复使用。
示例 1:
输入:board = [["o","a","a","n"],["e","t","a","e"],["i","h","k","r"],["i","f","l","v"]], words = ["oath","pea","eat","rain"]
输出:["eat","oath"]
示例 2:
输入:board = [["a","b"],["c","d"]], words = ["abcb"]
输出:[]
提示:
m == board.length
n == board[i].length
1 <= m, n <= 12
board[i][j]
是一个小写英文字母1 <= words.length <= 3 * 104
1 <= words[i].length <= 10
words[i]
由小写英文字母组成words
中的所有字符串互不相同
回溯
class Solution {
int[][] grid = {{0,1}, {0,-1}, {1,0}, {-1,0}};
int n,m;
Set<String> set;
boolean[][] vis;
List<String> res = new ArrayList<>();
public List<String> findWords(char[][] board, String[] words) {
set = new HashSet<>();
for(String word : words) set.add(word);
n = board.length;
m = board[0].length;
vis = new boolean[n+1][m+1];
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
StringBuilder sb = new StringBuilder();
sb.append(board[i][j]);
vis[i][j] = true;
dfs(i,j,board,sb);
vis = new boolean[n][m];
}
}
return res;
}
public void dfs(int i, int j, char[][] board, StringBuilder sb){
if(sb.length() > 10) return;
if(set.contains(sb.toString())){
res.add(sb.toString());
set.remove(sb.toString()); // 防止list重复的新方法
}
for(int k = 0; k < 4; k++){
int newx = i + grid[k][0];
int newy = j + grid[k][1];
if(newx >= 0 && newx < n && newy >= 0 && newy < m && vis[newx][newy] == false){
sb.append(board[newx][newy]);
vis[newx][newy] = true;
dfs(newx, newy, board, sb);
vis[newx][newy] = false;
sb.deleteCharAt(sb.length()-1);
}
}
}
}
字典树
class Solution {
public List<String> findWords(char[][] board, String[] words) {
//构建字典树
WordTrie myTrie = new WordTrie();
TrieNode root = myTrie.root;
//插入数据
for (String word : words){
myTrie.insert(word);
}
//构建结果集容器
List<String> result = new LinkedList<>();
//矩阵行数
int m = board.length;
//矩阵列数
int n = board[0].length;
//存储该节点是否访问
boolean[][] visited = new boolean[n][m];
//遍历整个二维数组
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
find(board, visited, i, j, m, n, result, root);
}
}
return result;
}
private void find(char[][] board, boolean[][] visited, int i, int j, int m, int n, List<String> result, TrieNode cur) {
//边界判断以及是否已经访问判断
if (i < 0 || i >= m || j < 0 || j >= n || visited[j][i])
return;
//获取子节点状态,判断其是否有子节点
cur = cur.child[board[i][j] - 'a'];
if (cur == null) {
return;
}
//修改节点状态,防止重复访问
visited[j][i] = true;
//找到单词加入
if (cur.isEnd) {
result.add(cur.val);
//找到单词后,修改字典树内叶子节点状态为false,防止出现重复单词
cur.isEnd = false;
}
find(board, visited, i + 1, j, m, n, result, cur);
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);
//最后修改节点状态为未访问状态
visited[j][i] = false;
}
/**
* 字典树
*/
class WordTrie {
//创建根节点
TrieNode root = new TrieNode();
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.isEnd = true;
cur.val = s;
}
}
/**
* 字典树节点
*/
class TrieNode {
/**
* 存储最后节点的字符串
*/
String val;
/**
* 根据字符排序,[a,b,c,……,z]
*/
TrieNode[] child = new TrieNode[26];
/**
* 是否是最后叶子节点
*/
boolean isEnd = false;
}
}