要求:在矩阵相邻找字典里单词
思路:
法一:暴力,从矩阵每个点开始向四个方向找,字典单词先哈希表存起来。O(mn4^10)
class Solution {
private:
unordered_set<string> us;
vector<vector<char>> board;
vector<pair<int, int>> direction;
int rows;
int cols;
bool visited[12][12];
vector<string> ans;
public:
vector<string> findWords(vector<vector<char>>& _board, vector<string>& words) {
board = _board;
direction = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};
rows = board.size();
cols = board[0].size();
for (string word : words) {
us.insert(word);
}
string path;
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
visited[r][c] = true;
path += board[r][c];
dfs(r, c, path);
path = "";
visited[r][c] = false;
}
}
return ans;
}
void dfs(int row, int col, string& path) {
if (path.length() > 10) return;
if (us.count(path) == 1) {
ans.push_back(path);
us.erase(path);
}
for (pair<int, int> dir : direction) {
int nr = row + dir.first;
int nc = col + dir.second;
if (0 <= nr && nr < rows && 0 <= nc && nc < cols) {
if (!visited[nr][nc]) {
visited[nr][nc] = true;
path += board[nr][nc];
dfs(nr, nc, path);
path.erase(path.size() - 1);
visited[nr][nc] = false;
}
}
}
}
};
法二:字典树的作用就是有才遍历,而不是一直无脑哈希。
class Trie {
public:
Trie* son[26]; // 存放当前字符之后的字符
bool isWord;
string str;
Trie() {
for (int i = 0; i < 26; i++) son[i] = nullptr;
isWord = false;
str = "";
}
void insert(string word) {
Trie* root = this; // 从头节点开始查
for (char c : word) { // 类似链表的遍历
int cur = c - 'a';
if (root->son[cur] == nullptr) root->son[cur] = new Trie();
root = root->son[cur];
}
root->isWord = true; // 在单词的结尾节点标记一下 是单词
root->str = word; // 结尾直接记录单词
}
};
class Solution {
public:
vector<vector<char>> board;
Trie trie;
int rows;
int cols;
vector<pair<int, int>> direction;
bool visited[12][12];
unordered_set<string> ans_set;
vector<string> ans;
vector<string> findWords(vector<vector<char>>& _board, vector<string>& words) {
board = _board;
rows = board.size();
cols = board[0].size();
direction = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};
for (string word : words) {
trie.insert(word);
}
for (int r = 0; r < rows; ++r) {
for (int c = 0; c < cols; ++c) {
int cur = board[r][c] - 'a';
if (trie.son[cur]) {
visited[r][c] = true;
dfs(r, c, trie.son[cur]);
visited[r][c] = false;
}
}
}
for (string s : ans_set) ans.push_back(s);
return ans;
}
void dfs(int row, int col, Trie* node) {
if (node->str != "") {
ans_set.insert(node->str);
}
for (pair<int, int> dir : direction) {
int nr = row + dir.first;
int nc = col + dir.second;
if (0 <= nr && nr < rows && 0 <= nc && nc < cols) {
if (!visited[nr][nc]) {
int nidx = board[nr][nc] - 'a';
if (node->son[nidx] != nullptr) {
visited[nr][nc] = true;
dfs(nr, nc, node->son[nidx]);
visited[nr][nc] = false;
}
}
}
}
}
};
下面官解,优化了visit数组用#代替,字典树另一写法
struct TrieNode {
string word;
unordered_map<char,TrieNode *> children;
TrieNode() {
this->word = "";
}
};
void insertTrie(TrieNode * root,const string & word) {
TrieNode * node = root;
for (auto c : word){
if (!node->children.count(c)) {
node->children[c] = new TrieNode();
}
node = node->children[c];
}
node->word = word;
}
class Solution {
public:
int dirs[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
bool dfs(vector<vector<char>>& board, int x, int y, TrieNode * root, set<string> & res) {
char ch = board[x][y];
if (!root->children.count(ch)) {
return false;
}
root = root->children[ch];
if (root->word.size() > 0) {
res.insert(root->word);
}
board[x][y] = '#';
for (int i = 0; i < 4; ++i) {
int nx = x + dirs[i][0];
int ny = y + dirs[i][1];
if (nx >= 0 && nx < board.size() && ny >= 0 && ny < board[0].size()) {
if (board[nx][ny] != '#') {
dfs(board, nx, ny, root,res);
}
}
}
board[x][y] = ch;
return true;
}
vector<string> findWords(vector<vector<char>> & board, vector<string> & words) {
TrieNode * root = new TrieNode();
set<string> res;
vector<string> ans;
for (auto & word: words){
insertTrie(root,word);
}
for (int i = 0; i < board.size(); ++i) {
for (int j = 0; j < board[0].size(); ++j) {
dfs(board, i, j, root, res);
}
}
for (auto & word: res) {
ans.emplace_back(word);
}
return ans;
}
};