Given a set of words (without duplicates), find all word squares you can build from them.
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).
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:
- There are at least 1 and at most 1000 words.
- All words will have the exact same length.
- Word length is at least 1 and at most 5.
- Each word contains only lowercase English alphabet
a-z
.
Example 1:
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).
思路:用Trie基本没有异议。但我的想法是在判断words是否合法时用prefix检验。用map记录首字符和单词list,在取首单词时用map检验,也是做了几个优化,但还不是最优解。先看我的AC解。
Runtime:448ms,beats:7.46%
class TrieNode { public: string word; TrieNode* child[26]; TrieNode() { word = ""; for (int i = 0; i < 26; i++) child[i] = nullptr; } }; class Trie { private: TrieNode * root; public: Trie(vector<string> words) { root = new TrieNode(); buildTrie(words); } void buildTrie(vector<string> words) { for (auto s : words) { TrieNode* tmpnode = root; for (auto c : s) { int idx = c - 'a'; if (!tmpnode->child[idx]) tmpnode->child[idx] = new TrieNode(); tmpnode = tmpnode->child[idx]; } tmpnode->word = s; } } bool hasprefix(string prefix) { TrieNode* tmpnode = root; for (auto c : prefix) { if (!tmpnode->child[c - 'a']) return false; tmpnode = tmpnode->child[c - 'a']; } return true; } bool hasword(string word) { TrieNode* tmpnode = root; for (auto c : word) { if (!tmpnode->child[c - 'a']) return false; tmpnode = tmpnode->child[c - 'a']; } return tmpnode->word != ""; } }; bool isvalid(vector<string> words, Trie& trie) { int l = words.size(); for (int i = 0; i < l; i++) { string prefix = ""; for (int j = 0; j < l; j++) { prefix += words[j][i]; } if (prefix != words[i].substr(0, prefix.size())) { return false; } } for(int i=l; i<words[0].size();i++){ string prefix = ""; for(int j=0; j<l; j++){ prefix += words[j][i]; } if(!trie.hasprefix(prefix)) return false; } return true; } void dfs(vector<vector<string>>& ret, unordered_map<char, vector<string>>& map, vector<string>& path, Trie& trie, set<string>& used, int idx) { if (idx == path[0].size()) { ret.push_back(path); return; } vector<string> wordlist = map[path[0][idx]]; for (int i = 0; i<wordlist.size(); i++) { if (used.count(wordlist[i])) continue; //used.insert(wordlist[i]); path.push_back(wordlist[i]); if (isvalid(path, trie)) { dfs(ret, map, path, trie, used, idx + 1); } path.pop_back(); //used.erase(wordlist[i]); } } class Solution { public: vector<vector<string>> wordSquares(vector<string>& words) { sort(words.begin(),words.end()); Trie trie = Trie(words); vector<vector<string>> ret; set<string> used; unordered_map<char, vector<string>> map; for (auto s : words) map[s[0]].push_back(s); for (auto s : words) { bool ban = false; for (char c : s) { if (!map.count(c)) { ban = true; break; } } if (ban) continue; vector<string> path; path.push_back(s); //used.insert(s); dfs(ret, map, path, trie, used, 1); //used.erase(s); } return ret; } };
下面网上看到的最快的一个解法。它的TrieNode还带了一个vector,记录该Node的prefix index,好处就是在DFS的时候,能直接找到该节点的前缀vector,在这个vector中遍历进行下一层DFS即可。
Runtime: 16ms beats: 99.63%
class Solution { struct TrieNode { vector<int> prefix; TrieNode* childs[26]; TrieNode() { memset(childs, 0, sizeof(childs)); } }; TrieNode* build(vector<string> words) { TrieNode* root = new TrieNode(); for (int i = 0; i < words.size(); i++) { TrieNode* p = root; for (auto c : words[i]) { if (!p->childs[c - 'a']) p->childs[c-'a'] = new TrieNode(); p = p -> childs[c-'a']; p->prefix.push_back(i); } } return root; } void helper(vector<vector<string>>& ret, vector<string>& board, vector<string>& words, TrieNode* root, int row) { if (row == words[0].size()) { ret.push_back(board); return; } TrieNode* tmp = root; for (int i = 0; i < row; i++) { if (!tmp->childs[board[i][row] - 'a']) return; tmp = tmp->childs[board[i][row] - 'a']; } for (int i : tmp->prefix) { board[row] = words[i]; helper(ret, board, words, root, row + 1); } } public: vector<vector<string>> wordSquares(vector<string>& words) { sort(words.begin(),words.end()); int n = words[0].size(); TrieNode* root = build(words); vector<vector<string>> ret; vector<string> board(n); for (int i = 0; i < words.size(); i++) { board[0] = words[i]; helper(ret, board, words, root, 1); } return ret; } };