1、題目描述
給定一個二維網格 board 和一個字典中的單詞列表 words,找出所有同時在二維網格和字典中出現的單詞。
單詞必須按照字母順序,通過相鄰的單元格內的字母構成,其中“相鄰”單元格是那些水平相鄰或垂直相鄰的單元格。同一個單元格內的字母在一個單詞中不允許被重複使用。
提示:
你需要優化回溯算法以通過更大數據量的測試。你能否早點停止回溯?
如果當前單詞不存在於所有單詞的前綴中,則可以立即停止回溯。什麼樣的數據結構可以有效地執行這樣的操作?散列表是否可行?爲什麼? 前綴樹如何?如果你想學習如何實現一個基本的前綴樹,請先查看這個問題: 實現Trie(前綴樹)。
說明:你可以假設所有輸入都由小寫字母 a-z 組成。
2、示例
輸入:
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"]
3、題解
基本思想:前綴樹+深度優先搜索遞歸回溯
1、前綴樹的結構是R個指向子結點的鏈接,其中每個鏈接對應字母表數據集中的一個字母,和一個字符串字段當截止該節點構成一個單詞就將該節點的字符串字段置爲該單詞,這樣如果截止該節點字符串字段不爲空說明遍歷截止該節點構成一個單詞可以直接加入res。之前的做法是給dfs額外一個string形參保存當前dfs遍歷過的字符,減慢函數調用速度
2、遍歷到當前節點構成一個單詞加入res後,置該節點字符串字段爲空,這樣防止res出現重複單詞。之前的做法是設置res爲set,不如這個方法好,這個方法可以避免遍歷board中重複單詞。
3、對於board中一次dfs已訪問過的字符不能再次訪問解決方法:置已訪問過的board中字符爲'*',dfs結束後再恢復。之前做法是設置visited二維數組,這樣增加了dfs形參個數佔用更多內存。
4、注意截止當前節點已經構成一個單詞後,可以繼續dfs搜索其他單詞,因爲可能出現一個單詞是另一個單詞的前綴情況,不能提前結束dfs。
#include<iostream>
#include<algorithm>
#include<vector>
#include<unordered_set>
using namespace std;
struct Trie
{
Trie* child[26];
string word = "";
Trie() {
for (int i = 0; i < 26; i++)
child[i] = nullptr;
}
};
class Solution {
public:
vector<string> res;
vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {
//基本思想:前綴樹+深度優先搜索遞歸回溯
//1、前綴樹的結構是R個指向子結點的鏈接,其中每個鏈接對應字母表數據集中的一個字母,
//和一個字符串字段當截止該節點構成一個單詞就將該節點的字符串字段置爲該單詞,
//這樣如果截止該節點字符串字段不爲空說明遍歷截止該節點構成一個單詞可以直接加入res。之前的做法是給dfs額外一個string形參保存當前dfs遍歷過的字符,減慢函數調用速度
//2、遍歷到當前節點構成一個單詞加入res後,置該節點字符串字段爲空,這樣防止res出現重複單詞。之前的做法是設置res爲set,不如這個方法好,這個方法可以避免遍歷board中重複單詞。
//3、對於board中一次dfs已訪問過的字符不能再次訪問解決方法:置已訪問過的board中字符爲'*',dfs結束後再恢復。之前做法是設置visited二維數組,這樣增加了dfs形參個數佔用更多內存。
//4、注意截止當前節點已經構成一個單詞後,可以繼續dfs搜索其他單詞,因爲可能出現一個單詞是另一個單詞的前綴情況,不能提前結束dfs。
Trie* t = new Trie();
//創建前綴樹,將words中所有單詞加入前綴樹
for (int i = 0; i < words.size(); i++)
{
Trie* cur = t;
for (int j = 0; j < words[i].size(); j++)
{
if (cur->child[words[i][j] - 'a'] == nullptr)
cur->child[words[i][j] - 'a'] = new Trie();
cur = cur->child[words[i][j] - 'a'];
}
cur->word = words[i];
}
for (int i = 0; i < board.size(); i++)
{
for (int j = 0; j < board[i].size(); j++)
{
dfs(board, t, i, j);
}
}
return res;
}
void dfs(vector<vector<char>>& board, Trie* t,int i ,int j)
{
if (i < 0 || j < 0 || i >= board.size() || j >= board[0].size())
return;
char c = board[i][j];
if (c == '*' || t->child[c-'a']==nullptr)
return;
t = t->child[c - 'a'];
if (t->word != "")
{
res.push_back(t->word);
t->word = "";
}
board[i][j] = '*';
dfs(board, t, i + 1, j);
dfs(board, t, i - 1, j);
dfs(board, t, i, j + 1);
dfs(board, t, i, j - 1);
board[i][j] = c;
return;
}
};
int main()
{
Solution solute;
vector<vector<char>> board = { {'a','b'},{'a','a'} };
vector<string> words = { "aaab","aaa" };
vector<string> res = solute.findWords(board, words);
for_each(res.begin(), res.end(), [](const auto v) {cout << v << endl; });
return 0;
}