单词搜索 II
给定一个二维网格 board 和一个字典中的单词列表 words,找出所有同时在二维网格和字典中出现的单词。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母在一个单词中不允许被重复使用。
示例:
输入:
words = [“oath”,“pea”,“eat”,“rain”] and board =
[
\quad
[‘o’,‘a’,‘a’,‘n’],
\quad
[‘e’,‘t’,‘a’,‘e’],
\quad
[‘i’,‘h’,‘k’,‘r’],
\quad
[‘i’,‘f’,‘l’,‘v’]
]
输出: [“eat”,“oath”]
说明:
你可以假设所有输入都由小写字母 a-z 组成。
提示:
你需要优化回溯算法以通过更大数据量的测试。你能否早点停止回溯?
如果当前单词不存在于所有单词的前缀中,则可以立即停止回溯。什么样的数据结构可以有效地执行这样的操作?散列表是否可行?为什么? 前缀树如何?如果你想学习如何实现一个基本的前缀树,请先查看这个问题: 实现Trie(前缀树)。
思路:
对于此类的单词搜索,我们首先想到的就是前缀树,提示1中的更大数据量的测试也提示了我们使用前缀树。
对于此类需要一个字母一个字母按顺序匹配的,我们采用深度优先遍历,此时需要考虑DFS的边界条件。
代码:
class Solution {
public:
//Trie节点定义
struct TrieNode {
bool isStr; //根结点至该节点是否为完整的字符串
string word; //若为完整的字符串,则其对应的字符串
TrieNode *next[26]; //下一个节点
};
vector<string> res; //存放结果
TrieNode *root=new TrieNode(); //前缀树根节点
//构建前缀树
void insert(string word)
{
TrieNode *cur=root;
for(auto & s:word)
{
if(cur->next[s-'a']==NULL)
{
cur->next[s-'a']=new TrieNode();
}
cur=cur->next[s-'a'];
}
cur->isStr=true;
cur->word=word;
}
//定义深度优先搜索
void DFS(vector<vector<char>>& board, int i, int j, TrieNode* root)
{
TrieNode*cur;
char tmp=board[i][j];
//递归边界
if(tmp=='1' || root->next[tmp-'a']==NULL)
return ; //已访问或者节点为空,则退出
//当前位置元素判断
cur=root->next[tmp-'a'];
if(cur->isStr) //若该节点时,恰好为一个字符串,则保存至结果
{
res.push_back(cur->word);
cur->isStr=false; //避免重复保存该字符串
}
//邻域检测
//这个已访问的标记需要写在第38行return的后面,否则可能导致下一次检测时,board中元素没有恢复
board[i][j]='1';
int tmp_i,tmp_j;
tmp_i=i-1;
if(tmp_i>=0)
DFS(board,tmp_i,j,cur);
tmp_i=i+1;
if(tmp_i<board.size())
DFS(board,tmp_i,j,cur);
tmp_j=j-1;
if(tmp_j>=0)
DFS(board,i,tmp_j,cur);
tmp_j=j+1;
if(tmp_j< board[0].size())
DFS(board,i,tmp_j,cur);
//恢复board中的元素,不要忘记
board[i][j]=tmp;
}
vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {
int row=board.size();
if(row==0)
return res;
int col=board[0].size();
if(col==0)
return res;
//建立前缀树
for(auto &word:words)
insert(word);
//搜索
for(int i=0;i<row;i++)
{
for(int j=0;j<col;j++)
{
DFS(board,i,j,root);
}
}
return res;
}
};
简化一下DFS:
class Solution {
public:
struct Trie
{
bool isWord=false;
string word="";
Trie* next[26]={nullptr};
};
Trie* root=new Trie();
int m,n;
vector<vector<int>> mark;
vector<string> res;
void addWord(string word)
{
Trie* cur=root;
for(auto c:word)
{
if(cur->next[c-'a']==nullptr)
cur->next[c-'a']=new Trie();
cur=cur->next[c-'a'];
}
cur->isWord=true;
cur->word=word;
}
void dfs(vector<vector<char>>& board, int i, int j, Trie* node)
{
if(i<0 || i>=m || j<0 || j>=n || mark[i][j] )
return ;
int ind=board[i][j]-'a';
if(node->next[ind]==nullptr)
return ;
if( node->next[ind]->isWord )
{
res.emplace_back(node->next[ind]->word);
node->next[ind]->isWord=false;
}
mark[i][j]=1;
dfs(board,i-1,j,node->next[ind]);
dfs(board,i+1,j,node->next[ind]);
dfs(board,i,j-1,node->next[ind]);
dfs(board,i,j+1,node->next[ind]);
mark[i][j]=0;
}
vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {
for(auto s:words)
addWord(s);
m=board.size();
if(m==0) return res;
n=board[0].size();
if(n==0) return res;
mark.resize(m,vector<int>(n));
for(int i=0;i<m;i++)
for(int j=0;j<n;j++)
dfs(board,i,j,root);
return res;
}
};
结果: