单词搜索 II
难度:
标签:
题目描述
给定一个 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
中的所有字符串互不相同
代码结果
运行时间: 468 ms, 内存: 0.0 MB
/*
* Java Stream API is not suitable for DFS/BFS operations as it's more suitable
* for functional-style operations on collections. However, we will use streams
* to collect the results at the end.
*/
import java.util.*;
import java.util.stream.Collectors;
public class Solution {
private static final int[][] DIRECTIONS = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
private int m, n;
private boolean[][] visited;
public List<String> findWords(char[][] board, String[] words) {
m = board.length;
n = board[0].length;
visited = new boolean[m][n];
Set<String> result = new HashSet<>();
TrieNode root = buildTrie(words);
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
dfs(board, i, j, root, result);
}
}
// Using Java Stream API to convert Set to List
return result.stream().collect(Collectors.toList());
}
private void dfs(char[][] board, int i, int j, TrieNode node, Set<String> result) {
char c = board[i][j];
if (c == '#' || node.next[c - 'a'] == null) return;
node = node.next[c - 'a'];
if (node.word != null) { // Found a word
result.add(node.word);
node.word = null; // Avoid duplicate results
}
board[i][j] = '#'; // Mark as visited
for (int[] dir : DIRECTIONS) {
int x = i + dir[0], y = j + dir[1];
if (x >= 0 && y >= 0 && x < m && y < n) {
dfs(board, x, y, node, result);
}
}
board[i][j] = c; // Restore original character
}
private TrieNode buildTrie(String[] words) {
TrieNode root = new TrieNode();
for (String word : words) {
TrieNode node = root;
for (char c : word.toCharArray()) {
int index = c - 'a';
if (node.next[index] == null) node.next[index] = new TrieNode();
node = node.next[index];
}
node.word = word;
}
return root;
}
class TrieNode {
TrieNode[] next = new TrieNode[26];
String word;
}
}
解释
方法:
这个题解使用了字典树(Trie)和深度优先搜索(DFS)的方法来解决单词搜索问题。首先,将所有需要搜索的单词构建成一棵字典树。然后,对于网格中的每个字符,从该字符开始进行DFS搜索。在DFS过程中,沿着字典树匹配字符,同时在网格上向四个方向扩展。如果在字典树的某个节点匹配到完整的单词,就将该单词加入到输出结果中。为了避免重复访问网格上的字符,使用一个visited数组来记录已访问过的位置。
时间复杂度:
O(m*n*4^w + k*w)
空间复杂度:
O(k*w)
代码细节讲解
🦆
为什么在字典树(Trie)中每个节点需要有一个`is_word`标志和`word`属性,这两者的作用分别是什么?
▷🦆
在DFS搜索过程中,为什么要使用一个`visited`列表来记录已访问的位置,而不是直接修改`board`上的字符?
▷🦆
DFS函数中,在尝试不同方向进行搜索前,为什么需要检查新的坐标`(new_x, new_y)`是否已经在`visited`列表中?
▷🦆
在实际操作中,如果`words`列表中的单词非常长,这会如何影响算法的性能?
▷相关问题
单词搜索
给定一个 m x n
二维字符网格 board
和一个字符串单词 word
。如果 word
存在于网格中,返回 true
;否则,返回 false
。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
示例 1:

输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED" 输出:true
示例 2:

输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "SEE" 输出:true
示例 3:

输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCB" 输出:false
提示:
m == board.length
n = board[i].length
1 <= m, n <= 6
1 <= word.length <= 15
board
和word
仅由大小写英文字母组成
进阶:你可以使用搜索剪枝的技术来优化解决方案,使其在 board
更大的情况下可以更快解决问题?
不同路径 III
在二维网格 grid
上,有 4 种类型的方格:
1
表示起始方格。且只有一个起始方格。2
表示结束方格,且只有一个结束方格。0
表示我们可以走过的空方格。-1
表示我们无法跨越的障碍。
返回在四个方向(上、下、左、右)上行走时,从起始方格到结束方格的不同路径的数目。
每一个无障碍方格都要通过一次,但是一条路径中不能重复通过同一个方格。
示例 1:
输入:[[1,0,0,0],[0,0,0,0],[0,0,2,-1]] 输出:2 解释:我们有以下两条路径: 1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2) 2. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2)
示例 2:
输入:[[1,0,0,0],[0,0,0,0],[0,0,0,2]] 输出:4 解释:我们有以下四条路径: 1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2),(2,3) 2. (0,0),(0,1),(1,1),(1,0),(2,0),(2,1),(2,2),(1,2),(0,2),(0,3),(1,3),(2,3) 3. (0,0),(1,0),(2,0),(2,1),(2,2),(1,2),(1,1),(0,1),(0,2),(0,3),(1,3),(2,3) 4. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2),(2,3)
示例 3:
输入:[[0,1],[2,0]] 输出:0 解释: 没有一条路能完全穿过每一个空的方格一次。 请注意,起始和结束方格可以位于网格中的任意位置。
提示:
1 <= grid.length * grid[0].length <= 20