Given a list of strings words
, write a function to find the longest word in words
such that every prefix of the word is also in words
. If there are multiple such words, return the lexicographically smallest one. If there is no such word, return an empty string.
Clarifications:
words
will only contain lowercase English letters.words
can vary.Examples:
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", which are all in the input words
.
Input: words = ["a", "banana", "app", "appl", "ap", "apply", "apple"]
Output: "apple"
Explanation: Both "apple" and "apply" can be built from other words in the list. "apple" is lexicographically smaller than "apply".
Input: words = ["m", "mo", "moc", "moch", "mocha", "l", "la", "lac", "lack"]
Output: "mocha"
Input: words = ["k", "ki", "kir", "kira", "kiran"]
Output: "kiran"
Input: words = ["a", "b", "c"]
Output: ""
Explanation: None of the words have prefixes that are present in the input.
Develop an algorithm that is efficient in terms of both time and space complexity. Consider using appropriate data structures to optimize your solution.
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
We want to find the longest word that can be built one letter at a time from other words in our list. The brute force approach checks every single word to see if its prefixes are also words in the list. If all prefixes of a word exist in the list, we consider that word as a candidate.
Here's how the algorithm would work step-by-step:
def longest_word_with_all_prefixes(word_list):
longest_word = ''
valid_candidate_words = []
for current_word in word_list:
is_candidate = True
# Iterate through prefixes of the current word.
for i in range(1, len(current_word) + 1):
prefix = current_word[:i]
# Check if the current prefix exists in the word list.
if prefix not in word_list:
is_candidate = False
break
# Add the current word to the candidates if all prefixes exist
if is_candidate:
valid_candidate_words.append(current_word)
# Determine the longest word from the valid candidates
for candidate_word in valid_candidate_words:
if len(candidate_word) > len(longest_word):
longest_word = candidate_word
elif len(candidate_word) == len(longest_word) and candidate_word < longest_word:
# Choose the lexicographically smaller word if lengths are the same
longest_word = candidate_word
return longest_word
To find the longest word made from other words in a list, we build a special data structure that lets us quickly check if a word is valid. This helps us efficiently explore potential words without wasting time on invalid ones.
Here's how the algorithm would work step-by-step:
def find_longest_word_with_all_prefixes(word_list):
prefix_set = set(word_list)
# Sort the words by length in descending order.
word_list.sort(key=len, reverse=True)
for current_word in word_list:
is_valid = True
# Check if all prefixes of the current word are in the set.
for i in range(1, len(current_word)):
prefix = current_word[:i]
if prefix not in prefix_set:
is_valid = False
break
# If all prefixes are present, return the word.
if is_valid:
return current_word
return ""
Case | How to Handle |
---|---|
Empty input list | Return an empty string since no word can be formed. |
Input list contains only one word | Return that single word, as it's trivially the longest and its own prefix. |
Input list contains words with different casing (e.g., 'a', 'A') | Normalize the case of all words (e.g., lowercase) to ensure case-insensitive comparison. |
No word is a prefix of another word in the list | Return an empty string if no word's prefixes are all present in the input. |
Multiple words have the same maximum length and are valid solutions | Return the lexicographically smallest longest word among the valid solutions. |
Input contains non-alphabetic characters | Filter the words to only include alphabetic characters or handle non-alphabetic characters based on the specific problem context. |
Very long words in the input list (potential memory issues) | Use a Trie data structure for efficient prefix checking and memory usage. |
Words that are prefixes of themselves (e.g., an empty string) | Handle empty strings explicitly by either excluding them from the input or defining their prefix status clearly based on the problem requirements. |