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;
  }
}