Given a set of words (without duplicates), find all word squares you can build from them.html
A sequence of words forms a valid word square if the kth row and column read the exact same string, where 0 ≤ k < max(numRows, numColumns).java
For example, the word sequence ["ball","area","lead","lady"]
forms a word square because each word reads the same both horizontally and vertically.數據結構
b a l l a r e a l e a d l a d y
Note:函數
a-z
.
Example 1:post
Input: ["area","lead","wall","lady","ball"] Output: [ [ "wall", "area", "lead", "lady" ], [ "ball", "area", "lead", "lady" ] ] Explanation: The output consists of two word squares. The order of output does not matter (just the order of words in each word square matters).
Example 2:ui
Input: ["abat","baba","atan","atal"] Output: [ [ "baba", "abat", "baba", "atan" ], [ "baba", "abat", "baba", "atal" ] ] Explanation: The output consists of two word squares. The order of output does not matter (just the order of words in each word square matters).
這道題是以前那道Valid Word Square的延伸,因爲要求出全部知足要求的單詞平方,因此難度大大的增長了,不要幻想着能夠利用以前那題的解法來暴力破解,OJ不會答應的。那麼根據以往的經驗,對於這種要打印出全部狀況的題的解法大多都是用遞歸來解,那麼這題的關鍵是根據前綴來找單詞,咱們若是能利用合適的數據結構來創建前綴跟單詞之間的映射,使得咱們能快速的經過前綴來判斷某個單詞是否存在,這是解題的關鍵。對於創建這種映射,這裏主要有兩種方法,一種是利用哈希表來創建前綴和全部包含此前綴單詞的集合以前的映射,第二種方法是創建前綴樹Trie,顧名思義,前綴樹專門就是爲這種問題設計的。那麼咱們首先來看第一種方法,用哈希表來創建映射的方法,咱們就是取出每一個單詞的全部前綴,而後將該單詞加入該前綴對應的集合中去,而後咱們創建一個空的nxn的char矩陣,其中n爲單詞的長度,咱們的目標就是來把這個矩陣填滿,咱們從0開始遍歷,咱們先取出長度爲0的前綴,即空字符串,因爲咱們在創建映射的時候,空字符串也和每一個單詞的集合創建了映射,而後咱們遍歷這個集合,用遍歷到的單詞的i位置字符,填充矩陣mat[i][i],而後j從i+1出開始遍歷,對應填充矩陣mat[i][j]和mat[j][i],而後咱們根據第j行填充獲得的前綴,到哈希表中查看有沒單詞,若是沒有,就break掉,若是有,則繼續填充下一個位置。最後若是j==n了,說明第0行和第0列都被填好了,咱們再調用遞歸函數,開始填充第一行和第一列,依次類推,直至填充完成,參見代碼以下:spa
解法一:設計
class Solution { public: vector<vector<string>> wordSquares(vector<string>& words) { vector<vector<string>> res; unordered_map<string, set<string>> m; int n = words[0].size(); for (string word : words) { for (int i = 0; i < n; ++i) { string key = word.substr(0, i); m[key].insert(word); } } vector<vector<char>> mat(n, vector<char>(n)); helper(0, n, mat, m, res); return res; } void helper(int i, int n, vector<vector<char>>& mat, unordered_map<string, set<string>>& m, vector<vector<string>>& res) { if (i == n) { vector<string> out; for (int j = 0; j < n; ++j) out.push_back(string(mat[j].begin(), mat[j].end())); res.push_back(out); return; } string key = string(mat[i].begin(), mat[i].begin() + i); for (string str : m[key]) { mat[i][i] = str[i]; int j = i + 1; for (; j < n; ++j) { mat[i][j] = str[j]; mat[j][i] = str[j]; if (!m.count(string(mat[j].begin(), mat[j].begin() + i + 1))) break; } if (j == n) helper(i + 1, n, mat, m, res); } } };
下面來看創建前綴樹Trie的方法,這種方法的難點是看能不能熟練的寫出Trie的定義,還有構建過程,以及後面在遞歸函數中,若是利用前綴樹來快速查找單詞的前綴,總之,這道題是前綴樹的一種經典的應用,能白板寫出來就說明基本上已經掌握了前綴樹了,參見代碼以下:code
解法二:orm
class Solution { public: struct TrieNode { vector<int> indexs; vector<TrieNode*> children; TrieNode(): children(26, nullptr) {} }; TrieNode* buildTrie(vector<string>& words) { TrieNode *root = new TrieNode(); for (int i = 0; i < words.size(); ++i) { TrieNode *t = root; for (int j = 0; j < words[i].size(); ++j) { if (!t->children[words[i][j] - 'a']) { t->children[words[i][j] - 'a'] = new TrieNode(); } t = t->children[words[i][j] - 'a']; t->indexs.push_back(i); } } return root; } vector<vector<string>> wordSquares(vector<string>& words) { TrieNode *root = buildTrie(words); vector<string> out(words[0].size()); vector<vector<string>> res; for (string word : words) { out[0] = word; helper(words, 1, root, out, res); } return res; } void helper(vector<string>& words, int level, TrieNode* root, vector<string>& out, vector<vector<string>>& res) { if (level >= words[0].size()) { res.push_back(out); return; } string str = ""; for (int i = 0; i < level; ++i) { str += out[i][level]; } TrieNode *t = root; for (int i = 0; i < str.size(); ++i) { if (!t->children[str[i] - 'a']) return; t = t->children[str[i] - 'a']; } for (int idx : t->indexs) { out[level] = words[idx]; helper(words, level + 1, root, out, res); } } };
相似題目:
參考資料:
https://discuss.leetcode.com/topic/63646/java-53ms-dfs-hashmap
https://discuss.leetcode.com/topic/63390/70ms-concise-c-solution-using-trie-and-backtracking
https://discuss.leetcode.com/topic/63516/explained-my-java-solution-using-trie-126ms-16-16/2