212. Word Search II
题目说明
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"]
.
题目解释
本题的输入是一个字符矩阵和字符串数组。在矩阵中定义上下左右位置为相邻,可连成路径,对于每一个输入的字符串,需要检测能否在字符矩阵中查找对应的路径,该路径连成的字符串等于输入字符串。将能够查找到的字符串返回。
思路
本题是一个很经典的
选择搜索出发点
我们需要确定
unordered_map<char, vector<pair<int, int> > >ump;
for (int i = 0; i < height; ++i)
for (int j = 0; j < width; ++j)
ump[board[i][j]].push_back(make_pair(i, j));
减少搜索复杂度
分析
使用传统的
abc
和abcd
,我们在搜索abc
的时候,假如没有搜索到,那我们在搜索abcd
的时候,我们已经不需要搜索就可以判断搜索不到了,因此这样就进行了无用的搜索。因此我们需要记录可以搜索到的字符串和不可搜索到的字符串,减小不必要的搜索。另外需要注意搜索字符串可能有重复,需要去重。
具体做法
对于每一个字符串,我们依次选择前1,2,3...i
个字符串进行搜索,如果搜索到某个字符串,我们记录下该子串可以搜到,某一个字符串搜不到,就记录下改子串搜不到。每次搜索的时,先查找是否有这个字符串的记录,如果有就不需要进行搜索了。具体的代码如下:
unordered_map<string, int> usi; // 记录不能通过的样例
unordered_map<string, int> ysi; // 记录能通过的样例
unordered_map<char, vector<pair<int, int> > >ump;
for (auto s : words)
{
char c = s[0];
vector<pair<int, int> > vp = ump[c];
// 将字符串从短到长依次搜索
for (int i = 1; i <= s.size(); i++)
{
string str = s.substr(0, i);
if (usi[str]) // 如果已经有一个片段无法搜索到,则不需要再延伸搜索
break;
if (ysi[str]) // 如果这个片段可以搜索到,如果已经是全部,需要记录下来,因为可能有更长的段搜不到,但部分是可以搜到的
{
if(i==s.size())
vs.push_back(str);
continue;
}
bool f = false;
// 从所有位置开始进行搜索
for (auto p : vp)
{
vector<vector<bool> >vvb(height, vector<bool>(width, false));
vvb[p.first][p.second] = true;
if (solve(str, p.first, p.second, 1, vvb, board))
{
if (i==s.size())
vs.push_back(str);
f = true;
break;
}
}
// 记录能否搜索到
if (!f)
usi[str] ++;
else
ysi[str] ++;
}
}
完整代码
class Solution {
public:
int dire[4][2] = { { 0, 1 }, { 0, -1 }, { 1, 0 }, { -1, 0 } };
int height, width;
// dfs搜索
bool solve(string s, int x, int y, int idx, vector<vector<bool> > &vvb, vector<vector<char>>& board)
{
if (idx == s.size())
return true;
bool f = false;
for (int i = 0; i < 4; i++)
{
int x0 = x + dire[i][0];
int y0 = y + dire[i][1];
if (x0 >= 0 && x0 < height && y0 >= 0 && y0 < width)
{
if (s[idx] == board[x0][y0] && vvb[x0][y0]==false)
{
vvb[x0][y0] = true;
if (solve(s, x0, y0, idx + 1, vvb, board))
return true;
vvb[x0][y0] = false;
}
}
}
return false;
}
vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {
unordered_map<string, int> usi; // 记录不能通过的样例
unordered_map<string, int> ysi; // 记录能通过的样例
vector<string> vs;
height = board.size();
if (height == 0)
return vs;
width = board[0].size();
// 先进行去重
unordered_map<string, int> ui;
for (auto s : words)
ui[s]++;
words.clear();
for (auto it = ui.begin(); it != ui.end(); ++it)
words.push_back(it->first);
// 存取每个字母的位置,用于选择出发点
unordered_map<char, vector<pair<int, int> > >ump;
for (int i = 0; i < height; ++i)
for (int j = 0; j < width; ++j)
ump[board[i][j]].push_back(make_pair(i, j));
for (auto s : words)
{
char c = s[0];
vector<pair<int, int> > vp = ump[c];
// 将字符串从短到长依次搜索
for (int i = 1; i <= s.size(); i++)
{
string str = s.substr(0, i);
if (usi[str]) // 如果已经有一个片段无法搜索到,则不需要再延伸搜索
break;
if (ysi[str]) // 如果这个片段可以搜索到,如果已经是全部,需要记录下来,因为可能有更长的段搜不到,但部分是可以搜到的
{
if(i==s.size())
vs.push_back(str);
continue;
}
bool f = false;
// 从所有位置开始进行搜索
for (auto p : vp)
{
vector<vector<bool> >vvb(height, vector<bool>(width, false));
vvb[p.first][p.second] = true;
if (solve(str, p.first, p.second, 1, vvb, board))
{
if (i==s.size())
vs.push_back(str);
f = true;
break;
}
}
// 记录能否搜索到
if (!f)
usi[str] ++;
else
ysi[str] ++;
}
}
return vs;
}
};