Longest Valid Word with All Prefixes
Last Updated :
03 Jul, 2025
Given an array of strings arr[], find the longest string such that all of its prefixes are also present in arr[]. If there are multiple such strings with the same maximum length, return the lexicographically smallest one.
Note: A prefix of a string is any substring starting from the first character, including the string itself.
Examples:
Input: arr[] = ["ab", "a", "abc", "abd"]
Output: abc
Explanation: Each of "abc" and "abd" has all its prefixes in the array. ("abc" → prefixes: "a", "ab", "abc " and "abd" → prefixes: "a", "ab", "abd"). Since both satisfy the condition and are of equal length, we return the lexicographically smaller one, which is "abc".
Input: arr[] = ["p", "pr", "pro", "probl", "problem", "pros", "process", "processor"]
Output: "pros"
Explanation: "pros" is the longest word with all prefixes present in the array(i.e., "p", "pr", "pro" , "pros")
[Naive Approach] Using Sorting and Binary Search
The idea is to sort the words lexicographically, then iterate through each word and check if all of its prefixes (including itself) exist in the list using binary search. The longest valid word is tracked, and in case of a tie, the lexicographically smaller one is chosen.
C++
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
string longestString(vector<string>& arr) {
// sort the arr for Binary Search to work correctly
sort(arr.begin(), arr.end());
string result;
// check each word
for (string& word : arr) {
bool isValid = true;
string prefix;
// check all prefixes of the word
for (char ch : word) {
prefix += ch;
// if the prefix is not in the array
if (!binary_search(arr.begin(), arr.end(), prefix)) {
isValid = false;
break;
}
}
// update result if current word is valid
// and better than current result
if (isValid &&
(word.size() > result.size() ||
(word.size() == result.size() && word < result))) {
result = word;
}
}
return result;
}
int main() {
vector<string> arr = {"ab", "a", "abc", "abd"};
cout << longestString(arr) << endl;
return 0;
}
Java
import java.util.Arrays;
class GfG {
public static String longestString(String[] arr) {
// sort the arr for Binary Search to work correctly
Arrays.sort(arr);
String result = "";
// check each word
for (String word : arr) {
boolean isValid = true;
String prefix = "";
// check all prefixes of the word
for (char ch : word.toCharArray()) {
prefix += ch;
// if the prefix is not in the array
if (Arrays.binarySearch(arr, prefix) < 0) {
isValid = false;
break;
}
}
// update result if current word is valid
// and better than current result
if (isValid &&
(word.length() > result.length() ||
(word.length() == result.length() &&
word.compareTo(result) < 0))) {
result = word;
}
}
return result;
}
public static void main(String[] args) {
String[] arr = {"ab", "a", "abc", "abd"};
System.out.println(longestString(arr));
}
}
Python
def longestString(arr):
# sort the arr for Binary Search to work correctly
arr.sort()
result = ""
# check each word
for word in arr:
isValid = True
prefix = ""
# check all prefixes of the word
for ch in word:
prefix += ch
# if the prefix is not in the array
if prefix not in arr:
isValid = False
break
# update result if current word is valid
# and better than current result
if isValid and (
len(word) > len(result) or
(len(word) == len(result) and word < result)):
result = word
return result
if __name__ == "__main__":
arr = ["ab", "a", "abc", "abd"]
print(longestString(arr))
C#
using System;
using System.Collections.Generic;
class GfG {
public static string longestString(string[] arr) {
// sort the arr for Binary Search to work correctly
Array.Sort(arr);
string result = "";
// check each word
foreach (string word in arr) {
bool isValid = true;
string prefix = "";
// check all prefixes of the word
foreach (char ch in word) {
prefix += ch;
// if the prefix is not in the array
if (Array.BinarySearch(arr, prefix) < 0) {
isValid = false;
break;
}
}
// update result if current word is valid
// and better than current result
if (isValid &&
(word.Length > result.Length ||
(word.Length == result.Length &&
string.Compare(word, result) < 0))) {
result = word;
}
}
return result;
}
public static void Main(string[] args) {
string[] arr = { "ab", "a", "abc", "abd" };
Console.WriteLine(longestString(arr));
}
}
JavaScript
function longestString(arr) {
// sort the arr for Binary Search to work correctly
arr.sort();
let result = "";
// check each word
for (let word of arr) {
let isValid = true;
let prefix = "";
// check all prefixes of the word
for (let ch of word) {
prefix += ch;
// if the prefix is not in the array
if (!arr.includes(prefix)) {
isValid = false;
break;
}
}
// update result if current word is valid
// and better than current result
if (isValid &&
(word.length > result.length ||
(word.length === result.length && word < result))) {
result = word;
}
}
return result;
}
// Driver Code
const arr = ["ab", "a", "abc", "abd"];
console.log(longestString(arr));
Time Complexity: O(n*k2*log n) for each word, of length up to k, generating k prefixes, with each requiring a binary search that cost O(k * log n) due to string comparison.
Auxiliary Space: O(1) as no extra data structure are used apart from a few variable.
[Better Approach] Rabin-Karp Based Prefix Validation
This approach uses the Rabin-Karp rolling hash technique with double hashing to uniquely represent each word and its prefixes using a pair of hash values, reducing collision risk. All word hashes are stored in a set. For each word, we incrementally compute prefix hashes using Rabin-Karp and check if all prefixes exist in the set. The longest valid word (or lexicographically smallest among equals) is returned.
Note: We avoid using only one hash because it increases the risk of hash collisions, which can cause incorrect prefix validation. Double hashing greatly reduces this risk by ensuring two independent hash checks.
C++
#include <iostream>
#include <vector>
#include <set>
#include <string>
#include <utility>
using namespace std;
const int base1 = 31;
const int mod1 = 1e9 + 7;
const int base2 = 37;
const int mod2 = 1e9 + 9;
// function to compute pair hash
vector<int> computeHash(string& s) {
int h1 = 0, h2 = 0;
int p1 = 1, p2 = 1;
for (char ch : s) {
int val = ch - 'a' + 1;
h1 = (h1 + val * 1LL * p1) % mod1;
h2 = (h2 + val * 1LL * p2) % mod2;
p1 = (p1 * 1LL * base1) % mod1;
p2 = (p2 * 1LL * base2) % mod2;
}
return { (int)h1, (int)h2 };
}
// check if all prefixes of word exist in hash set
bool allPrefixExist(string& word, set<vector<int>>& hashSet) {
int h1 = 0, h2 = 0;
int p1 = 1, p2 = 1;
for (int i = 0; i < word.size(); ++i) {
int val = word[i] - 'a' + 1;
h1 = (h1 + val * 1LL * p1) % mod1;
h2 = (h2 + val * 1LL * p2) % mod2;
if (hashSet.find({ (int)h1, (int)h2 }) == hashSet.end()) {
return false;
}
p1 = (p1 * 1LL * base1) % mod1;
p2 = (p2 * 1LL * base2) % mod2;
}
return true;
}
// function to get the longest word whose all prefixes exist
string longestString(vector<string>& arr) {
set<vector<int>> hashSet;
for (string& word : arr) {
hashSet.insert(computeHash(word));
}
string result = "";
// check for each word
for (string& word : arr) {
if (allPrefixExist(word, hashSet)) {
// update result if word is longer or
// lexicographically smaller
if (word.length() > result.length() ||
(word.length() == result.length() && word < result)) {
result = word;
}
}
}
return result;
}
int main() {
vector<string> arr = {
"ab", "a", "abc", "abd"
};
cout << longestString(arr) << endl;
return 0;
}
Java
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
class GfG {
static final int base1 = 31;
static final int mod1 = 1000000007;
static final int base2 = 37;
static final int mod2 = 1000000009;
// function to compute pair hash
static ArrayList<Integer> computeHash(String s) {
long h1 = 0, h2 = 0;
long p1 = 1, p2 = 1;
for (char ch : s.toCharArray()) {
int val = ch - 'a' + 1;
h1 = (h1 + val * p1) % mod1;
h2 = (h2 + val * p2) % mod2;
p1 = (p1 * base1) % mod1;
p2 = (p2 * base2) % mod2;
}
ArrayList<Integer> hash = new ArrayList<>();
hash.add((int) h1);
hash.add((int) h2);
return hash;
}
// check if all prefixes of word exist in hash set
static boolean allPrefixExist(String word, Set<ArrayList<Integer>> hashSet) {
long h1 = 0, h2 = 0;
long p1 = 1, p2 = 1;
for (int i = 0; i < word.length(); i++) {
int val = word.charAt(i) - 'a' + 1;
h1 = (h1 + val * p1) % mod1;
h2 = (h2 + val * p2) % mod2;
ArrayList<Integer> prefixHash = new ArrayList<>();
prefixHash.add((int) h1);
prefixHash.add((int) h2);
if (!hashSet.contains(prefixHash)) {
return false;
}
p1 = (p1 * base1) % mod1;
p2 = (p2 * base2) % mod2;
}
return true;
}
// function to get the longest word whose all prefixes exist
static String longestString(String[] arr) {
Set<ArrayList<Integer>> hashSet = new HashSet<>();
for (String word : arr) {
hashSet.add(computeHash(word));
}
String result = "";
// check for each word
for (String word : arr) {
if (allPrefixExist(word, hashSet)) {
// update result if word is longer or
// lexicographically smaller
if (word.length() > result.length() ||
(word.length() == result.length() && word.compareTo(result) < 0)) {
result = word;
}
}
}
return result;
}
public static void main(String[] args) {
String[] arr = {"ab", "a", "abc", "abd"};
System.out.println(longestString(arr));
}
}
Python
base1 = 31
mod1 = 10**9 + 7
base2 = 37
mod2 = 10**9 + 9
# function to compute pair hash
def computeHash(s):
h1 = 0
h2 = 0
p1 = 1
p2 = 1
for ch in s:
val = ord(ch) - ord('a') + 1
h1 = (h1 + val * p1) % mod1
h2 = (h2 + val * p2) % mod2
p1 = (p1 * base1) % mod1
p2 = (p2 * base2) % mod2
return (h1, h2)
# check if all prefixes of word exist in hash set
def allPrefixExist(word, hashSet):
h1 = 0
h2 = 0
p1 = 1
p2 = 1
for i in range(len(word)):
val = ord(word[i]) - ord('a') + 1
h1 = (h1 + val * p1) % mod1
h2 = (h2 + val * p2) % mod2
if (h1, h2) not in hashSet:
return False
p1 = (p1 * base1) % mod1
p2 = (p2 * base2) % mod2
return True
# function to get the longest word whose all prefixes exist
def longestString(arr):
hashSet = set()
for word in arr:
hashSet.add(computeHash(word))
result = ""
# check for each word
for word in arr:
if allPrefixExist(word, hashSet):
# update result if word is longer or
# lexicographically smaller
if len(word) > len(result) or (len(word) == len(result) and word < result):
result = word
return result
if __name__ == "__main__":
arr = ["ab", "a", "abc", "abd"]
print(longestString(arr))
C#
using System;
using System.Collections.Generic;
class GfG {
static int base1 = 31;
static int mod1 = 1000000007;
static int base2 = 37;
static int mod2 = 1000000009;
// function to compute pair hash
static int[] computeHash(string s) {
long h1 = 0, h2 = 0;
long p1 = 1, p2 = 1;
foreach (char ch in s) {
int val = ch - 'a' + 1;
h1 = (h1 + val * p1) % mod1;
h2 = (h2 + val * p2) % mod2;
p1 = (p1 * base1) % mod1;
p2 = (p2 * base2) % mod2;
}
return new int[] { (int)h1, (int)h2 };
}
// check if all prefixes of word exist in hash set
static bool allPrefixExist(string word, HashSet<string> hashSet) {
long h1 = 0, h2 = 0;
long p1 = 1, p2 = 1;
for (int i = 0; i < word.Length; i++) {
int val = word[i] - 'a' + 1;
h1 = (h1 + val * p1) % mod1;
h2 = (h2 + val * p2) % mod2;
string key = h1 + "," + h2;
if (!hashSet.Contains(key)) {
return false;
}
p1 = (p1 * base1) % mod1;
p2 = (p2 * base2) % mod2;
}
return true;
}
// function to get the longest word whose all prefixes exist
static string longestString(string[] arr) {
HashSet<string> hashSet = new HashSet<string>();
foreach (string word in arr) {
int[] hash = computeHash(word);
hashSet.Add(hash[0] + "," + hash[1]);
}
string result = "";
// check for each word
foreach (string word in arr) {
if (allPrefixExist(word, hashSet)) {
// update result if word is longer or
// lexicographically smaller
if (word.Length > result.Length ||
(word.Length == result.Length && string.Compare(word, result) < 0)) {
result = word;
}
}
}
return result;
}
static void Main(string[] args) {
string[] arr = { "ab", "a", "abc", "abd" };
Console.WriteLine(longestString(arr));
}
}
JavaScript
const base1 = 31;
const mod1 = 1e9 + 7;
const base2 = 37;
const mod2 = 1e9 + 9;
// function to compute pair hash
function computeHash(s) {
let h1 = 0, h2 = 0;
let p1 = 1, p2 = 1;
for (let i = 0; i < s.length; i++) {
let val = s.charCodeAt(i) - 'a'.charCodeAt(0) + 1;
h1 = (h1 + val * p1) % mod1;
h2 = (h2 + val * p2) % mod2;
p1 = (p1 * base1) % mod1;
p2 = (p2 * base2) % mod2;
}
return [Math.floor(h1), Math.floor(h2)];
}
// check if all prefixes of word exist in hash set
function allPrefixExist(word, hashSet) {
let h1 = 0, h2 = 0;
let p1 = 1, p2 = 1;
for (let i = 0; i < word.length; i++) {
let val = word.charCodeAt(i) - 'a'.charCodeAt(0) + 1;
h1 = (h1 + val * p1) % mod1;
h2 = (h2 + val * p2) % mod2;
let key = `${h1},${h2}`;
if (!hashSet.has(key)) {
return false;
}
p1 = (p1 * base1) % mod1;
p2 = (p2 * base2) % mod2;
}
return true;
}
// function to get the longest word whose all prefixes exist
function longestString(arr) {
let hashSet = new Set();
for (let word of arr) {
let [h1, h2] = computeHash(word);
hashSet.add(`${h1},${h2}`);
}
let result = "";
// check for each word
for (let word of arr) {
if (allPrefixExist(word, hashSet)) {
// update result if word is longer or
// lexicographically smaller
if (word.length > result.length ||
(word.length === result.length && word < result)) {
result = word;
}
}
}
return result;
}
// Driver Code
let arr = ["ab", "a", "abc", "abd"];
console.log(longestString(arr));
Time Complexity: O(n * k * log n) - for each word checking all k prefixes in the set (log n per prefix).
Auxiliary Space: O(n) - used to store the hash ( pair of integers ) of each word.
[Expected Approach] Trie-Based Prefix Validation
The idea is to insert all words into a Trie, marking the end of each word. Then, for each word, we check whether all its prefixes also exist as complete words in the Trie. If a word passes this check, we consider it as a candidate for the result. Among all valid candidates, we select the longest one, preferring lexicographically smaller words in case of a tie.
Algorithm:
- Define a TrieNode with 26 child pointers and a boolean isEnd.
- Insert every word from the array into the Trie.
- While inserting, create missing child nodes and mark isEnd = true at the last character.
- For each word, verify that every prefix is present in the Trie and has isEnd = true.
- Keep the longest word that passes this check; on ties choose the lexicographically smallest.
C++
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
// trie node definition
struct TrieNode {
TrieNode* children[26];
bool isEnd;
TrieNode() {
isEnd = false;
fill(begin(children), end(children), nullptr);
}
};
class Trie {
public:
TrieNode* root;
Trie() {
root = new TrieNode();
}
// insert a word into the trie
void insert(string& word) {
TrieNode* node = root;
for (char ch : word) {
int idx = ch - 'a';
if (!node->children[idx]) {
node->children[idx] = new TrieNode();
}
node = node->children[idx];
}
// marking the end of the word
node->isEnd = true;
}
// check if all prefixes of the word exist in the trie
bool allPrefixesExist(string& word) {
TrieNode* node = root;
for (char ch : word) {
int idx = ch - 'a';
node = node->children[idx];
// if the prefix is missing or not marked as end
if (!node || !node->isEnd) {
return false;
}
}
return true;
}
};
// Function to find the longest word whose
// all prefixes exist in the list
string longestString(vector<string>& arr) {
Trie trie;
// insert all words into the trie
for (string& word : arr) {
trie.insert(word);
}
string result = "";
// check each word
for (string& word : arr) {
// if all prefixes exist
if (trie.allPrefixesExist(word)) {
// update result if word is longer or
// lexicographically smaller
if (word.length() > result.length() ||
(word.length() == result.length() && word < result)) {
result = word;
}
}
}
return result;
}
int main() {
vector<string> arr = {"ab", "a", "abc", "abd"};
cout << longestString(arr) << endl;
return 0;
}
Java
import java.util.Arrays;
class TrieNode {
TrieNode[] children = new TrieNode[26];
boolean isEnd = false;
TrieNode() {
Arrays.fill(children, null);
}
}
class Trie {
TrieNode root;
Trie() {
root = new TrieNode();
}
// insert a word into the trie
void insert(String word) {
TrieNode node = root;
for (char ch : word.toCharArray()) {
int idx = ch - 'a';
if (node.children[idx] == null) {
node.children[idx] = new TrieNode();
}
node = node.children[idx];
}
// marking the end of the word
node.isEnd = true;
}
// check if all prefixes of the word exist in the trie
boolean allPrefixesExist(String word) {
TrieNode node = root;
for (char ch : word.toCharArray()) {
int idx = ch - 'a';
node = node.children[idx];
// if the prefix is missing or not marked as end
if (node == null || !node.isEnd) {
return false;
}
}
return true;
}
}
public class GfG {
// Function to find the longest word whose
// all prefixes exist in the list
public static String longestString(String[] arr) {
Trie trie = new Trie();
// insert all words into the trie
for (String word : arr) {
trie.insert(word);
}
String result = "";
// sort the arr to ensure lexicographical order
Arrays.sort(arr);
// check each word
for (String word : arr) {
// if all prefixes exist
if (trie.allPrefixesExist(word)) {
// update result if word is longer or
// lexicographically smaller
if (word.length() > result.length() ||
(word.length() == result.length() &&
word.compareTo(result) < 0)) {
result = word;
}
}
}
return result;
}
public static void main(String[] args) {
String[] arr = {"ab", "a", "abc", "abd"};
System.out.println(longestString(arr));
}
}
Python
# trie node definition
class TrieNode:
def __init__(self):
self.children = [None] * 26
self.isEnd = False
class Trie:
def __init__(self):
self.root = TrieNode()
# insert a word into the trie
def insert(self, word):
node = self.root
for ch in word:
idx = ord(ch) - ord('a')
if not node.children[idx]:
node.children[idx] = TrieNode()
node = node.children[idx]
# marking the end of the word
node.isEnd = True
# check if all prefixes of the word exist in the trie
def allPrefixesExist(self, word):
node = self.root
for ch in word:
idx = ord(ch) - ord('a')
node = node.children[idx]
# if the prefix is missing or not marked as end
if not node or not node.isEnd:
return False
return True
# Function to find the longest word whose
# all prefixes exist in the list
def longestString(words):
trie = Trie()
# insert all words into the trie
for word in arr:
trie.insert(word)
result = ""
# check each word
for word in arr:
# if all prefixes exist
if trie.allPrefixesExist(word):
# update result if word is longer or
# lexicographically smaller
if len(word) > len(result) or (len(word) == len(result) \
and word < result):
result = word
return result
if __name__ == "__main__":
arr = ["ab", "a", "abc", "abd"]
print(longestString(arr))
C#
using System;
class TrieNode {
public TrieNode[] children = new TrieNode[26];
public bool isEnd = false;
public TrieNode() {
for (int i = 0; i < 26; i++) {
children[i] = null;
}
}
}
class Trie {
public TrieNode root;
public Trie() {
root = new TrieNode();
}
// insert a word into the trie
public void Insert(string word) {
TrieNode node = root;
foreach (char ch in word) {
int idx = ch - 'a';
if (node.children[idx] == null) {
node.children[idx] = new TrieNode();
}
node = node.children[idx];
}
// marking the end of the word
node.isEnd = true;
}
// check if all prefixes of the word exist in the trie
public bool AllPrefixesExist(string word) {
TrieNode node = root;
foreach (char ch in word) {
int idx = ch - 'a';
node = node.children[idx];
// if the prefix is missing or not marked as end
if (node == null || !node.isEnd) {
return false;
}
}
return true;
}
}
class GfG {
// Function to find the longest word whose
// all prefixes exist in the list
public static string longestString(string[] arr) {
Trie trie = new Trie();
// insert all words into the trie
foreach (string word in arr) {
trie.Insert(word);
}
string result = "";
// check each word
foreach (string word in arr) {
// if all prefixes exist
if (trie.AllPrefixesExist(word)) {
// update result if word is longer or
// lexicographically smaller
if (word.Length > result.Length ||
(word.Length == result.Length &&
string.Compare(word, result) < 0)) {
result = word;
}
}
}
return result;
}
public static void Main(string[] args) {
string[] arr = { "ab", "a", "abc", "abd" };
Console.WriteLine(longestString(arr));
}
}
JavaScript
// trie node definition
class TrieNode {
constructor() {
this.children = new Array(26).fill(null);
this.isEnd = false;
}
}
class Trie {
constructor() {
this.root = new TrieNode();
}
// insert a word into the trie
insert(word) {
let node = this.root;
for (let ch of word) {
let idx = ch.charCodeAt(0) - 97;
if (!node.children[idx]) {
node.children[idx] = new TrieNode();
}
node = node.children[idx];
}
// marking the end of the word
node.isEnd = true;
}
// check if all prefixes of the word exist in the trie
allPrefixesExist(word) {
let node = this.root;
for (let ch of word) {
let idx = ch.charCodeAt(0) - 97;
node = node.children[idx];
// if the prefix is missing or not marked as end
if (!node || !node.isEnd) {
return false;
}
}
return true;
}
}
// Function to find the longest word whose
// all prefixes exist in the list
function longestString(arr) {
let trie = new Trie();
// insert all words into the trie
for (let word of arr) {
trie.insert(word);
}
let result = "";
// check each word
for (let word of arr) {
// if all prefixes exist
if (trie.allPrefixesExist(word)) {
// update result if word is longer or
// lexicographically smaller
if (word.length > result.length ||
(word.length === result.length && word < result)) {
result = word;
}
}
}
return result;
}
// Driver Code
const arr = ["ab", "a", "abc", "abd"];
console.log(longestString(arr));
Time Complexity: O(n*k), one pass for insertion and one for prefix checks where n is the number of words and k is the average word length,
Auxiliary Space: O(n*k), where n is the number of words and k is the average word length.
Explore
DSA Fundamentals
Data Structures
Algorithms
Advanced
Interview Preparation
Practice Problem