this problem remains me of soduku problem, because take a quick look at this problem, you will find that there seems have nothing to solve this besides tried every possible way. and we may use backtracking to elimenate unnecessary check.
and another important observation for this should be: every valid results should be symmterical.
//if I can write this in an inerview, I'm god
class TrieNode {
HashMap<Character, TrieNode> children = new HashMap<Character, TrieNode>();//use Hashmap to stores its children
List<Integer> wordList = new ArrayList<Integer>(); //stores the index of current character in which word
public TrieNode() {}
}
class Solution {
int N = 0;
String[] words = null;
TrieNode trie = null;
public List<List<String>> wordSquares(String[] words) { //the main solution of this problem
this.words = words;
this.N = words[0].length();
List<List<String>> results = new ArrayList<List<String>>();
this.buildTrie(words); //using given words we build the trie
for (String word : words) { //and we re-iterate words
LinkedList<String> wordSquares = new LinkedList<String>();
wordSquares.addLast(word);
this.backtracking(1, wordSquares, results); //backtraking the trie
}
return results;
}
protected void backtracking(int step, LinkedList<String> wordSquares, //step uses to keep track of the depth of recursion
List<List<String>> results) {
if (step == N) { //if we reached the bottom, means we have found a final results
results.add((List<String>) wordSquares.clone()); //add it
return;
}
StringBuilder prefix = new StringBuilder();
for (String word : wordSquares) { //for each word in current wordSquares
prefix.append(word.charAt(step)); //we append current positon of word to prefix
}
for (Integer wordIndex : this.getWordsWithPrefix(prefix.toString())) { //for every word with that prefix, we try to add it in wordSqaures to see if it is on
wordSquares.addLast(this.words[wordIndex]);
this.backtracking(step + 1, wordSquares, results);
wordSquares.removeLast(); //backtracking
}
}
protected void buildTrie(String[] words) { //build trie function:
this.trie = new TrieNode();
for (int wordIndex = 0; wordIndex < words.length; ++wordIndex) { //for every word in words
String word = words[wordIndex];
TrieNode node = this.trie; //re-initialize the node to the root of trie each time we want to put a new word in trie
for (Character letter : word.toCharArray()) {
if (node.children.containsKey(letter)) {
node = node.children.get(letter);
} else {
TrieNode newNode = new TrieNode();
node.children.put(letter, newNode);
node = newNode;
}
node.wordList.add(wordIndex); //the attribute of each node contains the index of word of current letter belongs to
}
}
}
protected List<Integer> getWordsWithPrefix(String prefix) { //return the word index with prefix is given prefix. this can be done just easily bu query
TrieNode node = this.trie;
for (Character letter : prefix.toCharArray()) {
if (node.children.containsKey(letter)) {
node = node.children.get(letter);
} else {
// return an empty list.
return new ArrayList<Integer>();
}
}
return node.wordList;
}
}