This problem asks us to find the lexicographically smallest longest string from a given array words
where all its prefixes are also present in words
. The most efficient approach utilizes a Trie data structure.
Trie (Prefix Tree):
A Trie is a tree-like data structure specifically designed for efficient prefix-based searches. Each node in the Trie represents a character, and paths from the root to a leaf node represent words. The structure allows for quick checking of whether a word or prefix exists.
Algorithm:
Trie Construction: We first construct a Trie from the input words
. Each word is inserted into the Trie. The isEnd
flag in each node indicates whether that node represents the end of a valid word.
Longest Word Search: We iterate through the words
array again. For each word:
isEnd
being false
at a node), we move on to the next word.longestWord
found so far. We update longestWord
if the current word is longer or if it's the same length but lexicographically smaller.Time Complexity:
w
in words
. This is because inserting a word takes time proportional to its length.Therefore, the overall time complexity is O(∑|w|), linear in the total number of characters in the input array.
Space Complexity:
The space complexity is dominated by the Trie. In the worst case, the Trie could store all prefixes of all words, leading to a space complexity of O(∑|w|).
Code Implementation (Python):
class TrieNode:
def __init__(self):
self.children = {} # Dictionary to store children nodes (key: character, value: TrieNode)
self.isEnd = False
class Solution:
def longestWord(self, words: List[str]) -> str:
root = TrieNode()
for word in words:
node = root
for char in word:
if char not in node.children:
node.children[char] = TrieNode()
node = node.children[char]
node.isEnd = True # Mark the end of the word in the Trie
longestWord = ""
for word in words:
isValid = True
node = root
for char in word:
if char not in node.children or not node.children[char].isEnd:
isValid = False
break
node = node.children[char]
if isValid:
if len(word) > len(longestWord) or (len(word) == len(longestWord) and word < longestWord):
longestWord = word
return longestWord
The code in other languages (Java, C++, Go, TypeScript, Rust, C#) follows a similar structure, with the Trie implementation and search algorithm adapted to the specific language's features. The core idea remains the same: build a Trie, then efficiently search for the longest word with all prefixes present in the Trie.