题目:
给定一个二维网格 board 和一个字典中的单词列表 words,找出所有同时在二维网格和字典中出现的单词。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母在一个单词中不允许被重复使用。
示例:
输入:
words = [“oath”,“pea”,“eat”,“rain”] and board =
[
[‘o’,‘a’,‘a’,‘n’],
[‘e’,‘t’,‘a’,‘e’],
[‘i’,‘h’,‘k’,‘r’],
[‘i’,‘f’,‘l’,‘v’]
]
输出: [“eat”,“oath”]
说明:
你可以假设所有输入都由小写字母 a-z 组成。
提示:
你需要优化回溯算法以通过更大数据量的测试。你能否早点停止回溯?
如果当前单词不存在于所有单词的前缀中,则可以立即停止回溯。什么样的数据结构可以有效地执行这样的操作?散列表是否可行?为什么? 前缀树如何?如果你想学习如何实现一个基本的前缀树,请先查看这篇文章。
思路
先将所有words插入前缀树;
对二维网格 board 每一个位置作为首字母开始动态规划查询当前单词是否在前缀树中,如果前缀树中有以该单词开头的则在可移动的上下左右范围内继续动态查找,如果前缀树中查到该单词则直接加入最后要返回的查到的单词的vector中,若在前缀树中查询不到以该单词开头的词则立即停止回溯,继续下一个位置为首字母的查找;
最后注意所有查到的单词的vector要去重。
vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {
wordslist.clear();
a = board;
int n = board.size();
int m = board[0].size();
used.resize(n, vector<bool>(m,false));
if(n > 0 && m > 0 && words.size() > 0) {
Trie* T = new Trie();
// 插入前缀树
for(int k = 0; k < words.size(); k++) {
T->insert(words[k]);
}
// 对二维网格 board 每一个位置作为首字母开始动态规划查询
for(int i = 0; i < board.size(); i++) {
for(int j = 0; j < board[0].size(); j++) {
findchars(i, j, T, "");
}
}
delete T;
}
// 去重,unique()函数将重复的元素放到vector的尾部 然后返回指向第一个重复元素的迭代器 再用erase函数擦除从这个元素到最后元素的所有的元素
sort(wordslist.begin(),wordslist.end());
wordslist.erase(unique(wordslist.begin(), wordslist.end()), wordslist.end());
return wordslist;
}
所有代码:
class Solution {
public:
class TrieNode {
public:
TrieNode* children[26];
string item = "";
};
class Trie {
private:
TrieNode* root;
public:
/** Initialize your data structure here. */
Trie() {
root = new TrieNode();
}
/** Inserts a word into the trie. */
void insert(string word) {
TrieNode* node = this->root;
for(int i = 0; i < word.length(); i++) {
if(node->children[word[i]-'a'] == NULL) {
node->children[word[i]-'a'] = new TrieNode();
}
node = node->children[word[i]-'a'];
}
node->item = word;
}
/** Returns if the word is in the trie. */
bool search(string word) {
TrieNode* node = this->root;
for(int i = 0; i < word.length(); i++) {
if(node->children[word[i]-'a'] == NULL) {
return false;
}
node = node->children[word[i]-'a'];
}
return node->item == word;
}
/** Returns if there is any word in the trie that starts with the given prefix. */
bool startsWith(string prefix) {
TrieNode* node = this->root;
for(int i = 0; i < prefix.length(); i++) {
if(node->children[prefix[i]-'a'] == NULL) {
return false;
}
node = node->children[prefix[i]-'a'];
}
return true;
}
};
vector<string> wordslist;
vector<vector<bool>> used;
vector<vector<char>> a;
vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {
wordslist.clear();
a = board;
int n = board.size();
int m = board[0].size();
used.resize(n, vector<bool>(m,false));
if(n > 0 && m > 0 && words.size() > 0) {
Trie* T = new Trie();
// 插入前缀树
for(int k = 0; k < words.size(); k++) {
T->insert(words[k]);
}
// 对二维网格 board 每一个位置作为首字母开始动态规划查询
for(int i = 0; i < board.size(); i++) {
for(int j = 0; j < board[0].size(); j++) {
findchars(i, j, T, "");
}
}
delete T;
}
//去重,unique()函数将重复的元素放到vector的尾部 然后返回指向第一个重复元素的迭代器 再用erase函数擦除从这个元素到最后元素的所有的元素
sort(wordslist.begin(),wordslist.end());
wordslist.erase(unique(wordslist.begin(), wordslist.end()), wordslist.end());
return wordslist;
}
// 动态规划查询部分
void findchars(int i, int j, Trie* t, string curstr) {
curstr += a[i][j];
used[i][j] = true;
if(t->search(curstr)) {
wordslist.push_back(curstr);
}
if(t->startsWith(curstr)) {
if(i > 0 && used[i-1][j] != true) {
findchars(i-1, j, t, curstr);
}
if(i < a.size()-1 && used[i+1][j] != true) {
findchars(i+1, j, t, curstr);
}
if(j > 0 && used[i][j-1] != true) {
findchars(i, j-1, t, curstr);
}
if(j < a[0].size()-1 && used[i][j+1] != true) {
findchars(i, j+1, t, curstr);
}
}
used[i][j] = false;
return;
}
};