Given a list of contacts contact[] and a query string s, implement a search for the phone directory.
For every prefix of s, starting from the first character and extending one character at a time, find all distinct contacts that start with that prefix.
The matching contacts for each prefix must be returned in lexicographical increasing order. If no contact matches a prefix, return a list containing "0" for that prefix.
For example, if s = "geeips", the prefixes are ["g", "ge", "gee", "geei", "geeip", "geeips"]. The result should contain one list for each of these prefixes.
Examples:
Input: contact[] = {"geeikistest", "geeksforgeeks", "geeksfortest"}, s = "geeips"
Output:
[["geeikistest", "geeksforgeeks", "geeksfortest"],
["geeikistest", "geeksforgeeks", "geeksfortest"],
["geeikistest", "geeksforgeeks", "geeksfortest"],
["geeikistest"],
["0"],
["0"]]
Explanation:
For the prefix "g", all three contacts match, so all three are returned in lexicographical increasing order.
For the prefixes "ge" and "gee", all three contacts still match.
For the prefix "geei", only "geeikistest" matches.
No contact starts with "geeip" or "geeips", so ["0"] is returned for both prefixes.Input: contact[] = {"alice", "alex", "bob"}, s = "al"
Output:
[["alex", "alice"],
["alex", "alice"]]
Explanation:
For the prefix "a", the matching contacts are "alex" and "alice".
For the prefix "al", both contacts still match. They are returned in lexicographical increasing order.
The idea is to use a Trie to store all contacts because it is naturally designed for prefix-based searching.
Each node represents a prefix of one or more contacts, making it easy to find all contacts matching the current query prefix.
For every character of the query string, move through the Trie to reach the node representing the current prefix, then use DFS to collect all contacts below that node.
By visiting the child nodes from 'a' to 'z', the contacts are collected directly in lexicographical order. If a prefix does not exist in the Trie, no longer prefix can match, so we return "0" for that and all remaining prefixes.
Steps to implement the below code:
- Trie Construction: Insert all unique contacts into a Trie, where each node represents a prefix shared by one or more contacts.
- Prefix Traversal: Process the query string character by character and move through the Trie to reach the node representing the current prefix.
- Collect Suggestions: Perform DFS from the current node to find all contacts having the current prefix.
- Lexicographical Order: Visit child nodes from 'a' to 'z' during DFS, so matching contacts are collected in lexicographical order.
- Early Pruning: If the current prefix does not exist in the Trie, add "0" and return "0" for all remaining prefixes since they cannot have any matches.
#include <bits/stdc++.h>
using namespace std;
class TrieNode
{
public:
TrieNode *children[26];
bool isEnd;
TrieNode()
{
isEnd = false;
for (int i = 0; i < 26; i++)
children[i] = nullptr;
}
};
// Insert a contact into the Trie
void insert(TrieNode *root, string &word)
{
TrieNode *curr = root;
for (char ch : word)
{
int idx = ch - 'a';
if (curr->children[idx] == nullptr)
curr->children[idx] = new TrieNode();
curr = curr->children[idx];
}
curr->isEnd = true;
}
// Find all contacts having the given prefix
void dfs(TrieNode *curr, string &prefix, vector<string> &ans)
{
if (curr->isEnd)
ans.push_back(prefix);
// Visit children in lexicographical order
for (int i = 0; i < 26; i++)
{
if (curr->children[i] != nullptr)
{
prefix.push_back('a' + i);
dfs(curr->children[i], prefix, ans);
prefix.pop_back();
}
}
}
vector<vector<string>> displayContacts(vector<string> &contact, string &s)
{
TrieNode *root = new TrieNode();
// Build the Trie
for (string &word : contact)
insert(root, word);
vector<vector<string>> result;
TrieNode *curr = root;
string prefix = "";
// Process every prefix of s
for (char ch : s)
{
prefix.push_back(ch);
// If a previous prefix had no match,
// all remaining prefixes will also have no match.
if (curr == nullptr)
{
result.push_back({"0"});
continue;
}
int idx = ch - 'a';
// No contact has the current prefix
if (curr->children[idx] == nullptr)
{
result.push_back({"0"});
curr = nullptr;
continue;
}
// Move to the node representing the current prefix
curr = curr->children[idx];
vector<string> suggestions;
// Find all contacts having this prefix
dfs(curr, prefix, suggestions);
if (suggestions.empty())
result.push_back({"0"});
else
result.push_back(suggestions);
}
return result;
}
int main()
{
vector<string> contact = {"geeikistest", "geeksforgeeks", "geeksfortest"};
string s = "geeips";
vector<vector<string>> result = displayContacts(contact, s);
for (auto &list : result)
{
for (string &word : list)
cout << word << " ";
cout << "\n";
}
return 0;
}
import java.util.*;
class TrieNode {
TrieNode[] children;
boolean isEnd;
TrieNode()
{
children = new TrieNode[26];
isEnd = false;
}
}
class GFG {
// Insert a contact into the Trie
static void insert(TrieNode root, String word) {
TrieNode curr = root;
for (char ch : word.toCharArray()) {
int idx = ch - 'a';
if (curr.children[idx] == null)
curr.children[idx] = new TrieNode();
curr = curr.children[idx];
}
curr.isEnd = true;
}
// Find all contacts having the given prefix
static void dfs(TrieNode curr, StringBuilder prefix, ArrayList<String> ans) {
if (curr.isEnd)
ans.add(prefix.toString());
// Visit children in lexicographical order
for (int i = 0; i < 26; i++) {
if (curr.children[i] != null) {
prefix.append((char)('a' + i));
dfs(curr.children[i], prefix, ans);
prefix.deleteCharAt(prefix.length() - 1);
}
}
}
static ArrayList<ArrayList<String>> displayContacts(String contact[], String s) {
TrieNode root = new TrieNode();
// Build the Trie
for (String word : contact)
insert(root, word);
ArrayList<ArrayList<String> > result
= new ArrayList<>();
TrieNode curr = root;
StringBuilder prefix = new StringBuilder();
// Process every prefix of s
for (char ch : s.toCharArray()) {
prefix.append(ch);
// If a previous prefix had no match,
// all remaining prefixes will also have no
// match.
if (curr == null) {
result.add(
new ArrayList<>(Arrays.asList("0")));
continue;
}
int idx = ch - 'a';
// No contact has the current prefix
if (curr.children[idx] == null) {
result.add(
new ArrayList<>(Arrays.asList("0")));
curr = null;
continue;
}
// Move to the node representing the current
// prefix
curr = curr.children[idx];
ArrayList<String> suggestions = new ArrayList<>();
// Find all contacts having this prefix
dfs(curr, prefix, suggestions);
if (suggestions.isEmpty())
result.add(
new ArrayList<>(Arrays.asList("0")));
else
result.add(suggestions);
}
return result;
}
public static void main(String[] args)
{
String[] contact = { "geeikistest", "geeksforgeeks", "geeksfortest" };
String s = "geeips";
ArrayList<ArrayList<String> > result
= displayContacts(contact, s);
for (ArrayList<String> list : result) {
for (String word : list)
System.out.print(word + " ");
System.out.println();
}
}
}
class TrieNode:
def __init__(self):
self.children = [None] * 26
self.isEnd = False
# Insert a contact into the Trie
def insert(root, word):
curr = root
for ch in word:
idx = ord(ch) - ord('a')
if curr.children[idx] is None:
curr.children[idx] = TrieNode()
curr = curr.children[idx]
curr.isEnd = True
# Find all contacts having the given prefix
def dfs(curr, prefix, ans):
if curr.isEnd:
ans.append(prefix)
# Visit children in lexicographical order
for i in range(26):
if curr.children[i] is not None:
ch = chr(ord('a') + i)
dfs(curr.children[i], prefix + ch, ans)
def displayContacts(contact, s):
root = TrieNode()
# Build the Trie
for word in contact:
insert(root, word)
result = []
curr = root
prefix = ""
# Process every prefix of s
for ch in s:
prefix += ch
# If a previous prefix had no match,
# all remaining prefixes will also have no match.
if curr is None:
result.append(["0"])
continue
idx = ord(ch) - ord('a')
# No contact has the current prefix
if curr.children[idx] is None:
result.append(["0"])
curr = None
continue
# Move to the node representing the current prefix
curr = curr.children[idx]
suggestions = []
# Find all contacts having this prefix
dfs(curr, prefix, suggestions)
if not suggestions:
result.append(["0"])
else:
result.append(suggestions)
return result
# Driver Code
if __name__ == "__main__":
contact = [
"geeikistest",
"geeksforgeeks",
"geeksfortest"
]
s = "geeips"
result = displayContacts(contact, s)
for contacts in result:
print(*contacts)
using System;
using System.Collections.Generic;
class TrieNode {
public TrieNode[] children;
public bool isEnd;
public TrieNode()
{
children = new TrieNode[26];
isEnd = false;
}
}
class GFG {
// Insert a contact into the Trie
static void Insert(TrieNode root, string word)
{
TrieNode curr = root;
foreach(char ch in word)
{
int idx = ch - 'a';
if (curr.children[idx] == null)
curr.children[idx] = new TrieNode();
curr = curr.children[idx];
}
curr.isEnd = true;
}
// Find all contacts having the given prefix
static void DFS(TrieNode curr, string prefix,
List<string> ans)
{
if (curr.isEnd)
ans.Add(prefix);
// Visit children in lexicographical order
for (int i = 0; i < 26; i++) {
if (curr.children[i] != null) {
char ch = (char)('a' + i);
DFS(curr.children[i], prefix + ch, ans);
}
}
}
public List<List<string> >
displayContacts(string[] contact, string s)
{
TrieNode root = new TrieNode();
// Build the Trie
foreach(string word in contact) Insert(root, word);
List<List<string> > result = new List<List<string> >();
TrieNode curr = root;
string prefix = "";
// Process every prefix of s
foreach(char ch in s) {
prefix += ch;
// If a previous prefix had no match,
// all remaining prefixes will also have no
// match.
if (curr == null) {
result.Add(new List<string>{ "0" });
continue;
}
int idx = ch - 'a';
// No contact has the current prefix
if (curr.children[idx] == null) {
result.Add(new List<string>{ "0" });
curr = null;
continue;
}
// Move to the node representing the current
// prefix
curr = curr.children[idx];
List<string> suggestions = new List<string>();
// Find all contacts having this prefix
DFS(curr, prefix, suggestions);
if (suggestions.Count == 0)
result.Add(new List<string>{ "0" });
else
result.Add(suggestions);
}
return result;
}
static void Main()
{
string[] contact = { "geeikistest", "geeksforgeeks", "geeksfortest" };
string s = "geeips";
GFG obj = new GFG();
List<List<string> > result
= obj.displayContacts(contact, s);
foreach(List<string> list in result)
{
foreach(string word in list)
Console.Write(word + " ");
Console.WriteLine();
}
}
}
class TrieNode {
constructor()
{
this.children = new Array(26).fill(null);
this.isEnd = false;
}
}
// Insert a contact into the Trie
function insert(root, word)
{
let curr = root;
for (let ch of word) {
let idx = ch.charCodeAt(0) - 97;
if (curr.children[idx] === null)
curr.children[idx] = new TrieNode();
curr = curr.children[idx];
}
curr.isEnd = true;
}
// Find all contacts having the given prefix
function dfs(curr, prefix, ans)
{
if (curr.isEnd)
ans.push(prefix);
// Visit children in lexicographical order
for (let i = 0; i < 26; i++) {
if (curr.children[i] !== null) {
let ch = String.fromCharCode(97 + i);
dfs(curr.children[i], prefix + ch, ans);
}
}
}
function displayContacts(contact, s)
{
let root = new TrieNode();
// Build the Trie
for (let word of contact)
insert(root, word);
let result = [];
let curr = root;
let prefix = "";
// Process every prefix of s
for (let ch of s) {
prefix += ch;
// If a previous prefix had no match,
// all remaining prefixes will also have no match.
if (curr === null) {
result.push([ "0" ]);
continue;
}
let idx = ch.charCodeAt(0) - 97;
// No contact has the current prefix
if (curr.children[idx] === null) {
result.push([ "0" ]);
curr = null;
continue;
}
// Move to the node representing the current prefix
curr = curr.children[idx];
let suggestions = [];
// Find all contacts having this prefix
dfs(curr, prefix, suggestions);
if (suggestions.length === 0)
result.push([ "0" ]);
else
result.push(suggestions);
}
return result;
}
// Driver Code
function main()
{
let contact = [ "geeikistest", "geeksforgeeks", "geeksfortest" ];
let s = "geeips";
let result = displayContacts(contact, s);
for (let list of result) {
console.log(list.join(" "));
}
}
main();
Output
geeikistest geeksforgeeks geeksfortest geeikistest geeksforgeeks geeksfortest geeikistest geeksforgeeks geeksfortest geeikistest 0 0
Time Complexity: O(L × n × m), where n is the number of contacts, m is the maximum contact length, and L is the length of the query string. Building the Trie takes O(n × m) time. For each of the L prefixes, DFS can visit up to O(n × m) nodes in the worst case, giving O(L × n × m) time overall.
Auxiliary Space: O(n × m), as the Trie can contain up to n × m nodes in the worst case when the contacts have no common prefixes. The DFS recursion uses at most O(m) additional stack space, which is dominated by the Trie space.