Given a list of strings words representing an English Dictionary, find the longest word in words that can be built one character at a time by other words in words. If there is more than one possible answer, return the longest word with the smallest lexicographical order.
If there is no answer, return the empty string.
Example 1:

Input: 
words = ["w","wo","wor","worl", "world"]
Output: "world"
Explanation: 
The word "world" can be built one character at a time by "w", "wo", "wor", and "worl".

Example 2:

Input: 
words = ["a", "banana", "app", "appl", "ap", "apply", "apple"]
Output: "apple"
Explanation: 
Both "apply" and "apple" can be built from other words in the dictionary. However, "apple" is lexicographically smaller than "apply".

Note:
All the strings in the input will only contain lowercase letters.
The length of words will be in the range [1, 1000].
The length of words[i] will be in the range [1, 30].












Solution 1 :  
For each word, check if all prefixes word[:k] are present. We can use a Set structure to check this quickly.
When  there are multiple valid candidates , get the longest one, when there are several candidates of the same length, get the smallest lexicographical order, implementation is like this 
 if (word.length() > ans.length() ||
                    word.length() == ans.length() && word.compareTo(ans) < 0) {


Another slight different approach but the same general idea is to sort the input list first by the above requirement , and check word[:k] are present.

 Arrays.sort(words, (a, b) -> a.length() == b.length()
                    ? a.compareTo(b) : b.length() - a.length());



// without sorting 


class Solution{
    public String longestWord(String[] words){
        String res = "";
        Set<String> set = new HashSet();
        for(String word : words) set.add(word);
        for(String word : words){
            if(word.length() > res.length() || (word.length() == res.length() && word.compareTo(res) < 0)){
                boolean flag = true;
                for(int i = 1; i < word.length(); i++){
                    if(!set.contains(word.substring(0, i))){
                        flag = false;
                        break;
                    }
                }
                if(flag) res = word;
            }
        }
        return res;
    }
}



















// sorting 


// Arrays.sort() function , something wrong , couldn’t compile 
class Solution {
    public String longestWord(String[] words) {
        // sort the words by length, and if these words have the same length, order by smallest lexicographical order.
        
        // put the words into a set, and check every ordered words , 
        HashSet<String> set = new HashSet<>(words);
        Arrays.sort(words, new Comparator(String a, String b){
            if(a.length() == b.length()){
                return a.CompareTo(b);
            }else{
                return b.length() - a.length();
                    // sort by length , bigger one at the front 
            } 
        });
        
        for(String word : words){
            boolean flag = true;
            for(int i = 1; i < words.length; i++){
                if(!set.contains(word.substring(0, i))){
                    flag = false;
                    break;
                }
            }
            if(flag) return word;
        }
        return "";
    }
}



//  passed , pay attention to the lambda expression used in comparator 
class Solution{
    public String longestWord(String[] words){
        //HashSet<String> set = new HashSet(words); // ???
        HashSet<String> set = new HashSet<>();
        for(String word : words){
            set.add(word);
        }
        Arrays.sort(words, (a, b) -> a.length() == b.length() ? a.compareTo(b) : b.length() - a.length());
        for(String word : words){
            boolean flag = true;
            for(int i = 1; i < word.length(); i++){
                if(!set.contains(word.substring(0, i))){
                    flag = false;
                    break;
                }
            }
            if(flag) return word;
        }
        return "";
    }
}


https://leetcode.com/problems/longest-word-in-dictionary/solution/






Solution 2 :  trie + traversal (dfs or bfs )
Key point: related to prefix, prefix tree, one particular part of this question is to ensure the word is build one step at a time, so it requires that every node along the path is a word when traversing the trie tree 


trie + dfs 
Intuition
As prefixes of strings are involved, this is usually a natural fit for a trie (a prefix tree.)
Algorithm
Put every word in a trie, then depth-first-search from the start of the trie, only searching nodes that ended a word. Every node found (except the root, which is a special case) then represents a word with all it's prefixes present. We take the best such word.



https://leetcode.com/problems/longest-word-in-dictionary/solution/

// other’s code
class Solution {
    public String longestWord(String[] words) {
        Trie trie = new Trie();
        int index = 0;
        for (String word: words) {
            trie.insert(word, ++index); //indexed by 1
        }
        trie.words = words;
        return trie.dfs();
    }
}
class Node {
    char c;
    HashMap<Character, Node> children = new HashMap();
    int end;
    public Node(char c){
        this.c = c;
    }
}

class Trie {
    Node root;
    String[] words;
    public Trie() {
        root = new Node('0');
    }

    public void insert(String word, int index) {
        Node cur = root;
        for (char c: word.toCharArray()) {
            cur.children.putIfAbsent(c, new Node(c));
            cur = cur.children.get(c);
        }
        cur.end = index;
    }

    public String dfs() {
        String ans = "";
        Stack<Node> stack = new Stack();
        stack.push(root);
        while (!stack.empty()) {
            Node node = stack.pop();
            if (node.end > 0 || node == root) {
                if (node != root) {
                    String word = words[node.end - 1];
                    if (word.length() > ans.length() ||
                            word.length() == ans.length() && word.compareTo(ans) < 0) {
                        ans = word;
                    }
                }
                for (Node nei: node.children.values()) {
                    stack.push(nei);
                }
            }
        }
        return ans;
    }
}





: 
 trie  + bfs 
https://leetcode.com/problems/longest-word-in-dictionary/discuss/109113/Java-Solution-with-Trie-+-BFS

node.children[j].isWord