题目
Given a 2D board and a list of words from the dictionary, find all words in the board.
Each word must be constructed from letters of sequentially adjacent cell, where “adjacent” cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once in a word.
For example,
Given words = [“oath”,”pea”,”eat”,”rain”] and board =
[
[‘o’,’a’,’a’,’n’],
[‘e’,’t’,’a’,’e’],
[‘i’,’h’,’k’,’r’],
[‘i’,’f’,’l’,’v’]
]
Return [“eat”,”oath”].
Note:
You may assume that all inputs are consist of lowercase letters a-z.
click to show hint.
You would need to optimize your backtracking to pass the larger test. Could you stop backtracking earlier?
If the current candidate does not exist in all words’ prefix, you could stop backtracking immediately. What kind of data structure could answer such query efficiently? Does a hash table work? Why or why not? How about a Trie? If you would like to learn how to implement a basic trie, please work on this problem: Implement Trie (Prefix Tree) first.
题目来源:https://leetcode.com/problems/word-search-ii/
分析
有个hint就忍不住点开看,哈哈。输入为一个二维字母表和若干个单词,在二维字母表里面任一个字母开始,向某个方向(上下左右)走,匹配到给定的单词则输出。
根据提示,我们先将给定的单词建立成字典树,关于字典树的概念在这里(http://blog.csdn.net/u010902721/article/details/45749447)。然后深度优先搜索(dfs)二维字母表,在搜索过程中已经搜索到的字符串作为前序在字典树里面匹配不到就没必要再继续搜索了。
代码
代码分为两大部分,字典树的相关操作和深度优先搜索的代码。如果不熟悉字典树的话,一定要先看看字典树的概念和实现代码(http://blog.csdn.net/u010902721/article/details/45749447),字典树很简单的。
class TrieNode{//字典树的节点
public:
int val;//标识,如果为1则表示一个单词的最后一个字符。
TrieNode *next[26];
TrieNode(){//构造函数中初始化节点
this->val = 0;
for(int i = 0; i < 26; i++)
this->next[i] = NULL;
}
};
class Solution {
private:
TrieNode *root;//字典树的根节点
public:
Solution(){
this->root = new TrieNode;
}
void TrieInsert(string word){//将单词插入到字典树
TrieNode *p = root;
int len = word.length();
for(int i = 0; i < len; i++){
int d = word.at(i) - 'a';
if(p->next[d] == NULL){
p->next[d] = new TrieNode;
}
p = p->next[d];
}
p->val = 1;//一个单词插入结束
}
bool TrieSearchWord(string word){//在字典树中查找一个单词
TrieNode *p = root;
int len = word.length();
for(int i = 0; i < len; i++){
int d = word.at(i) - 'a';
if(p->next[d] == NULL)
return false;
else
p = p->next[d];
}
if(p->val == 1)
return true;
else
return false;
}
//在字典树中查找一个单词前序。如果当前已经搜索到的字符串不是字典树中的某个单词的前序,肯定就不用再继续dfs了。比如当前已经搜索到的字符串为"abc",但是字典树中没有单词以"abc"开头的,那肯定就不用再继续往下dfs了。
bool TrieSearchPre(string str){
TrieNode *p = root;
int len = str.length();
for(int i = 0; i < len; i++){
int d = str.at(i) - 'a';
if(p->next[d] == NULL)
return false;
else
p = p->next[d];
}
return true;
}
//深度优先搜索DFS
void dfs(vector<vector<char> >&board, vector<vector<bool> > &flag, set<string> &help, string &s, int x, int y){
int m = board.size();
int n = board[0].size();
int hx[] = {1,0,-1,0};//四个方向
int hy[] = {0,1,0,-1};
flag[x][y] = true;
for(int i = 0; i < 4; i++){
int xx = x + hx[i];
int yy = y + hy[i];
if(!(xx < 0 || xx >= m || yy < 0 || yy >= n || flag[xx][yy])){
s.push_back(board[xx][yy]);//将当前字符加入已搜索到的字符串中
if(TrieSearchWord(s))//字典树中有没有这个字符串
help.insert(s);//先放入set中,目的是去掉重复
if(TrieSearchPre(s))//如果当前字符串作为前序在字典树中匹配不到就不需要继续dfs
dfs(board, flag, help, s, xx, yy);
s.pop_back();//回溯
}
}
flag[x][y] = false;
}
vector<string> findWords(vector<vector<char> >& board, vector<string>& words) {
vector<string> result;
if (board.empty() || board[0].empty() || words.empty()) return result;
int len = words.size();
for(int i = 0; i < len; i++)//将所给单词建立字典树
TrieInsert(words[i]);
int m = board.size();
int n = board[0].size();
vector<vector<bool> > flag(m, vector<bool>(n, false));//已访问标识
string s;
set<string> help;//去除重复
for(int mi = 0; mi < m; mi++){
for(int nj = 0; nj < n; nj++){
s.push_back(board[mi][nj]);//将当前字符加入已搜索到的字符串中
if(TrieSearchWord(s))//字典树中有没有这个字符串
help.insert(s);//先放入set中,目的是去掉重复
if(TrieSearchPre(s))//如果当前字符串作为前序在字典树中匹配不到就不需要继续dfs
dfs(board, flag, help, s, mi, nj);
s.pop_back();//回溯
}
}
for(auto &word : help)
result.push_back(word);
return result;
}
};