Given an array of strings words, find the longest string in words such that every prefix of it is also in words.
For example, let words = ["a", "app", "ap"]. The string "app" has prefixes "ap" and "a", all of which are in words.
Return the string described above. If there is more than one string with the same length, return the lexicographically smallest one, and if no string exists, return "".
Example 1:
Input: words = ["k","ki","kir","kira", "kiran"]
Output: "kiran"
Explanation: "kiran" has prefixes "kira", "kir", "ki", and "k", and all of them appear in words.
Example 2:
Input: words = ["a", "banana", "app", "appl", "ap", "apply", "apple"]
Output: "apple"
Explanation: Both "apple" and "apply" have all their prefixes in words.
However, "apple" is lexicographically smaller, so we return that.
Example 3:
Input: words = ["abc", "bc", "ab", "qwe"]
Output: ""
Constraints:
1 <= words.length <= 105
1 <= words[i].length <= 105
1 <= sum(words[i].length) <= 105
words[i] consists only of lowercase English letters.
Solutions
Solution 1: Trie
Thinking
We want the longest word whose every prefix is also in the list, breaking ties lexicographically. Rechecking the list for each prefix would square the total length.
Insert every word into a trie and mark terminals. A word is valid iff every node on its path is a terminal. Compare valid words by length and then lexicographic order.
We define a Trie where each node has two attributes: a child node array \(\textit{children}\) of length \(26\), and a flag \(\textit{isEnd}\) indicating whether the node marks the end of a word.
We iterate over \(\textit{words}\), and for each word \(w\), we traverse from the root node. If the child node array of the current node does not contain the first character of \(w\), we create a new node, then continue traversing the next character of \(w\). After traversing all characters of \(w\), we set the \(\textit{isEnd}\) flag of the current node to \(\texttt{true}\).
Next, we iterate over \(\textit{words}\) again, and for each word \(w\), we traverse from the root node. If the \(\textit{isEnd}\) field of a node in the child node array is \(\texttt{false}\), it means some prefix of \(w\) is not in \(\textit{words}\), and we return \(\texttt{false}\). Otherwise, we continue traversing the next character of \(w\), and after traversing all characters, we return \(\texttt{true}\).
The time complexity is \(O(\sum_{w \in \textit{words}} |w|)\), and the space complexity is \(O(\sum_{w \in \textit{words}} |w|)\), where \(|w|\) is the length of word \(w\).