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).
Example 2:
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).
Solution:
build a trie so that it's easier to find strings with prefix. calculate the next prefix with current list of strings, for example "wall" => "a" "wall", "area" => "le" "wall", "area", "lead" => "lad"
class Solution {
class TrieNode {
TrieNode[] children = new TrieNode[26];
boolean isWord = false;
}
public void insert(TrieNode root, String w) {
for (int i = 0; i < w.length(); i ++) {
char c = w.charAt(i);
if (root.children[c - 'a'] == null) {
root.children[c - 'a'] = new TrieNode();
}
root = root.children[c - 'a'];
}
root.isWord = true;
}
public List<String> search(TrieNode root, String prefix) {
for (int i = 0; i < prefix.length(); i ++) {
char c = prefix.charAt(i);
if (root.children[c - 'a'] != null) {
root = root.children[c - 'a'];
} else {
return new ArrayList();
}
}
List<String> result = new ArrayList();
traverse(root, new StringBuilder(prefix), result);
return result;
}
private void traverse(TrieNode root, StringBuilder sb, List<String> result) {
if (root.isWord) {
result.add(sb.toString());
return;
}
for (char c = 'a'; c <= 'z'; c ++) {
if (root.children[c - 'a'] != null) {
sb.append(c);
traverse(root.children[c - 'a'], sb, result);
sb.deleteCharAt(sb.length() - 1);
}
}
}
public List<List<String>> wordSquares(String[] words) {
TrieNode root = new TrieNode();
for (String w : words) {
insert(root, w);
}
List<List<String>> result = new ArrayList();
dfs(root, words[0].length(), new ArrayList(), result);
return result;
}
private void dfs(TrieNode root, int len, List<String> curr, List<List<String>> result) {
if (curr.size() == len) {
result.add(new ArrayList(curr));
return;
}
String prefix = calcPrefix(curr);
List<String> possibles = search(root, prefix);
for (String next : possibles) {
curr.add(next);
dfs(root, len, curr, result);
curr.remove(curr.size() - 1);
}
}
private String calcPrefix(List<String> curr) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < curr.size(); i ++) {
String w = curr.get(i);
sb.append(w.charAt(curr.size()));
}
return sb.toString();
}
}