零、原题链接
一、题目描述
给定一个 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 中的所有字符串互不相同
三、解题思路
- 基本思路:
通过字典树来对深度搜索进行剪枝。 - 具体思路:
- 将
words
的字符构建成字典树; - 深度搜索
board
:- 如果下标违法,则返回;
- 如果该格子已经遍历过,则返回;
- 如果字典树不存在该路径,则返回;【剪枝】
- 如果字典树遍历到一个单词,则记录;
- 将当前格子设置为
'#'
;【防止重复遍历】 - 递归遍历邻居四个格子;
- 恢复当前格子原本的内容;
- 遍历字典树,将记录的单词存放到
ans
中; - 返回
ans
。
- 将
四、参考代码
时间复杂度:
O
(
m
⋅
n
⋅
3
l
−
1
)
\Omicron(m \cdot n \cdot 3^{l-1})
O(m⋅n⋅3l−1) 【构建字典树和遍历字典树的复杂度都没有深度遍历 board
高,而深度遍历 board ,每个格子都进行深度递归,有
m
⋅
n
m\cdot n
m⋅n 个格子,深度递归最长路径为单词最长长度
l
l
l,递归的复杂度为
4
⋅
3
l
−
1
4\cdot 3^{l-1}
4⋅3l−1 】
空间复杂度:
O
(
m
n
+
k
l
)
\Omicron(mn+kl)
O(mn+kl)【存放 board + 存放字典树】
struct Node {
Node* next[26] = {nullptr};
string word;
bool flag;
Node() : word(""), flag(false) {}
};
class Solution {
public:
vector<vector<char>> m_board;
vector<pair<int, int>> pos = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
int m, n;
Node* root;
vector<string> ans;
void dfs(const int& i, const int& j, Node* p) {
if (i < 0 || i >= m || j < 0 || j >= n) // 坐标不合法
return;
if (m_board[i][j] == '#') // 已经遍历过
return;
if (p->next[m_board[i][j] - 'a'] == nullptr)
return;
if (p->next[m_board[i][j] - 'a']->word.length() != 0) {
p->next[m_board[i][j] - 'a']->flag = true;
}
char t = m_board[i][j];
m_board[i][j] = '#';
for (int x = 0; x < pos.size(); x++) {
dfs(i + pos[x].first, j + pos[x].second, p->next[t - 'a']);
}
m_board[i][j] = t;
}
void setans(Node* p) {
if (p->flag)
ans.emplace_back(p->word);
for (int i = 0; i < 26; i++) {
if (p->next[i] == nullptr)
continue;
setans(p->next[i]);
}
}
vector<string> findWords(vector<vector<char>>& board,
vector<string>& words) {
m_board = board;
m = board.size();
n = board[0].size();
root = new Node();
// 构建字典树
for (int i = 0; i < words.size(); i++) {
Node* p = root;
for (int j = 0; j < words[i].size(); j++) {
if (p->next[words[i][j] - 'a'] == nullptr) {
p->next[words[i][j] - 'a'] = new Node();
}
p = p->next[words[i][j] - 'a'];
}
p->word = words[i];
}
// 确认是否存在
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
dfs(i, j, root);
}
}
setans(root);
return ans;
}
};