- Word Search II
中文English
Given a matrix of lower alphabets and a dictionary. Find all words in the dictionary that can be found in the matrix. A word can start from any position in the matrix and go left/right/up/down to the adjacent position. One character only be used once in one word. No same word in dictionary
Example
Example 1:
Input:[“doaf”,“agai”,“dcan”],[“dog”,“dad”,“dgdg”,“can”,“again”]
Output:[“again”,“can”,“dad”,“dog”]
Explanation:
d o a f
a g a i
d c a n
search in Matrix,so return [“again”,“can”,“dad”,“dog”].
Example 2:
Input:[“a”],[“b”]
Output:[]
Explanation:
a
search in Matrix,return [].
Challenge
Using trie to implement your algorithm.
解法1:
这题挺难的。我参考的网上的答案。
思路就是暴力深搜 - 每个格子来一次DFS深搜。
注意:
- visited[]数组还是需要
- 注意剪枝:如果不是prefix,剪掉! 如果出界或已经访问过,剪掉!
- char -> string 不能直接用std里面的to_string(),因为char本来就是integer,to_string(char)会生成"100","103"这样的字符串。可以用string(1,c)来生成一个临时的内容为c,长度为1的字符串。
- 对于二维数组或vector,用if (board.size() < 1) 比if (board.empty()) 更好
- getPrefixSets()里面,下面一行
if (prefixSets.find(prefix) == prefixSets.end())
很关键!因为有可能某个word刚好是之前某个prefix,这样可以避免该word被关联到false!
class Solution {
public:
/**
* @param board: A list of lists of character
* @param words: A list of string
* @return: A list of string
*/
vector<int> dx = {0, 1, -1, 0};
vector<int> dy = {1, 0, 0, -1};
map<string, bool> prefixSets; //放所有的prefix,如果是word就对应true,否则对应false
vector<string> wordSearchII(vector<vector<char>> &board, vector<string> &words) {
vector<string> result;
if (board.size() < 1) //比if (board.empty()) 更好?
return result;
vector<vector<bool>> visited;
for (int i = 0; i < board.size(); ++i)
visited.push_back(vector<bool>(board[i].size(), false));
getPrefixSets(words);
set<string> wordSet;
for (int i = 0; i < board.size(); ++i) {
for (int j = 0; j < board[i].size(); ++j) {
visited[i][j] = true;
//char->string 不可以用to_string,否则就是 100->"100"
helper(board, visited, i, j, string(1, board[i][j]), wordSet); //注意string(1,c)
visited[i][j] = false;
}
}
for (auto w : wordSet)
result.push_back(w);
return result;
}
private:
//generate the map
void getPrefixSets(vector<string> &words) {
for (auto w : words) {
for (int i = 0; i < w.size() - 1; ++i) { //不需要到w.size(),因为整个word下面会处理
string prefix = w.substr(0, i + 1); //前i + 1个字符
if (prefixSets.find(prefix) == prefixSets.end()) //关键!有可能某个word刚好是之前某个prefix,这样可以避免该word被关联到false!
prefixSets[prefix] = false;
}
prefixSets[w] = true;
}
}
void helper(vector<vector<char>> &board, vector<vector<bool>> &visited, int x, int y, string word, set<string> &wordSet) {
if (prefixSets.find(word) == prefixSets.end()) //如果不是prefix, 剪枝
return;
if (prefixSets[word])
wordSet.insert(word);
for (int i = 0; i < 4; ++i) {
int adjX = x + dx[i];
int adjY = y + dy[i];
if (!inside(board, adjX, adjY) || visited[adjX][adjY]) // 出界或已访问,剪枝
continue;
visited[adjX][adjY] = true;
helper(board, visited, adjX, adjY, word + board[adjX][adjY], wordSet);
visited[adjX][adjY] = false;
}
}
bool inside(vector<vector<char>> &board, int x, int y) {
return x >= 0 &&
x < board.size() &&
y >= 0 &&
y < board[0].size();
}
};
二刷:
class Solution {
public:
/**
* @param board: A list of lists of character
* @param words: A list of string
* @return: A list of string
* we will sort your return value in output
*/
vector<string> wordSearchII(vector<vector<char>> &board, vector<string> &words) {
unordered_set<string> wordSets;
unordered_set<string> preSets;
for (auto word : words) {
string pre = "";
for (int i = 0; i < word.size(); i++) {
pre = pre + word[i];
preSets.insert(pre);
}
wordSets.insert(pre);
}
unordered_set<string> sols;
for (int i = 0; i < board.size(); i++) {
for (int j = 0; j < board[0].size(); j++) {
vector<vector<bool>> visited(board.size(), vector<bool>(board[0].size(), false));
string sol(1, board[i][j]);
visited[i][j] = true;
helper(board, wordSets, preSets, i, j, visited, sol, sols);
visited[i][j] = false;
sol.pop_back();
}
}
vector<string> res(sols.begin(), sols.end());
return res;
}
private:
void helper(vector<vector<char>> &board, unordered_set<string> &wordSets, unordered_set<string> &preSets,
int currX, int currY, vector<vector<bool>> &visited,
string sol, unordered_set<string> &sols) {
if (preSets.find(sol) == preSets.end()) return;
if (wordSets.find(sol) != wordSets.end() && sols.find(sol) == sols.end()) {
sols.insert(sol);
// return; //do not stop! continue to search
}
for (int i = 0; i < 4; i++) {
int newX = currX + dx[i];
int newY = currY + dy[i];
if (newX < 0 || newX >= board.size() ||
newY < 0 || newY >= board[0].size() ||
visited[newX][newY]) continue;
//sol = sol + board[newX][newY];
if (preSets.find(sol) != preSets.end()) {
visited[newX][newY] = true;
helper(board, wordSets, preSets, newX, newY, visited, sol + board[newX][newY], sols);
visited[newX][newY] = false;
//sol.pop_back(); //important
} //else {
// sol.pop_back();
//}
}
return;
}
int dx[4] = {0, 0, -1, 1};
int dy[4] = {-1, 1, 0, 0};
};
解法2:trie+dfs
struct Node {
Node *ch[26];
string str;
Node() {
for (int i = 0; i < 26; i++) {
ch[i] = nullptr;
}
str = "";
}
};
class Solution {
public:
/**
* @param board: A list of lists of character
* @param words: A list of string
* @return: A list of string
* we will sort your return value in output
*/
vector<string> wordSearchII(vector<vector<char>> &board, vector<string> &words) {
Node *trieRoot = new Node();
for (auto word : words) {
insert(trieRoot, word);
}
unordered_set<string> sols;
for (int i = 0; i < board.size(); i++) {
for (int j = 0; j < board[0].size(); j++) {
vector<vector<bool>> visited(board.size(), vector<bool>(board[0].size(), false));
if (trieRoot->ch[board[i][j] - 'a'] != nullptr) {
visited[i][j] = true;
search(board, trieRoot->ch[board[i][j] - 'a'], i, j, visited, sols);
visited[i][j] = false;
}
}
}
vector<string> res(sols.begin(), sols.end());
return res;
}
private:
void insert(Node *p, string s) {
int len = s.size();
for (int i = 0; i < len; i++) {
if (p->ch[s[i] - 'a'] == nullptr) {
p->ch[s[i] - 'a'] = new Node();
}
p = p->ch[s[i] - 'a'];
}
p->str = s; //store the string at the last node
// cout << " insert " << p->str << endl;
}
void search(vector<vector<char>> &board, Node *node, int x, int y, vector<vector<bool>> &visited, unordered_set<string> &sols) {
if (!node->str.empty()) {
sols.insert(node->str);
//p->str = ""; //can also clear p->str here so there would be no duplicate
//(i.e., we do not need to use set or unordered_set, vector<string> sols is OK)
}
for (int i = 0; i < 4; i++) {
int newX = x + dx[i];
int newY = y + dy[i];
if (newX < 0 || newX >= board.size() || newY < 0 || newY >= board[0].size() || visited[newX][newY]) continue;
if (node->ch[board[newX][newY] - 'a'] != nullptr) {
visited[newX][newY] = true;
search(board, node->ch[board[newX][newY] - 'a'], newX, newY, visited, sols);
visited[newX][newY] = false;
}
}
}
int dx[4] = {1, -1, 0, 0};
int dy[4] = {0, 0, 1, -1};
};
注意:
- 排重。否则下面的输入例子会有两个"dad"。
Explanation:
d o a f
a g a i
d c a n
排重可用set或unordered_set,然后再将结果传给一个vector返回。也可以在得到一个node->str后,将node->str设为"",这样下一次就不会再得到这个node->str。
二刷:
struct TrieNode {
bool isWord;
TrieNode * children[26];
TrieNode() : isWord(false) {
for (int i = 0; i < 26; i++) {
children[i] = NULL;
}
}
};
class Solution {
public:
/**
* @param board: A list of lists of character
* @param words: A list of string
* @return: A list of string
* we will sort your return value in output
*/
vector<string> wordSearchII(vector<vector<char>> &board, vector<string> &words) {
int nRow = board.size();
int nCol = board[0].size();
root = new TrieNode();
visited.resize(nRow, vector<bool>(nCol, false));
for (auto word : words) insertWord(word);
unordered_set<string> res;
for (int i = 0; i < nRow; i++) {
for (int j = 0; j < nCol; j++) {
string str = string(1, board[i][j]);
if (startWith(str)) {
visited[i][j] = true;
string str = string(1, board[i][j]);
helper(board, words, i, j, str, res, visited);
visited[i][j] = false;
}
}
}
vector<string> ret(res.begin(), res.end());
return ret;
}
private:
TrieNode *root;
int dx[4] = {1, -1, 0, 0};
int dy[4] = {0, 0, 1, -1};
vector<vector<bool>> visited;
void helper(vector<vector<char>> &board, vector<string> &words, int x, int y,
string str, unordered_set<string> &res, vector<vector<bool>> &visited) {
if (searchWord(str)) {
res.insert(str);
}
for (int i = 0; i < 4; i++) {
int newX = x + dx[i];
int newY = y + dy[i];
if (newX >= 0 && newX < board.size() && newY >= 0 && newY < board[0].size()
&& !visited[newX][newY]) {
str += board[newX][newY];
if (startWith(str)) {
visited[newX][newY] = true;
helper(board, words, newX, newY, str, res, visited);
visited[newX][newY] = false;
}
str.pop_back();
}
}
}
void insertWord(string& word) {
TrieNode *cur = root;
int n = word.size();
for (int i = 0; i < n; i++) {
if (!cur->children[word[i] - 'a']) cur->children[word[i] - 'a'] = new TrieNode();
cur = cur->children[word[i] - 'a'];
}
cur->isWord = true;
}
bool searchWord(string &str) {
TrieNode *cur = root;
int n = str.size();
for (int i = 0; i < n; i++) {
if (!cur->children[str[i] - 'a']) return false;
cur = cur->children[str[i] - 'a'];
}
if (cur->isWord) return true;
return false;
}
bool startWith(string &str) {
TrieNode *cur = root;
int n = str.size();
for (int i = 0; i < n; i++) {
if (!cur->children[str[i] - 'a']) return false;
cur = cur->children[str[i] - 'a'];
}
return true;
}
};