Implement a Phone Directory

Last Updated : 22 Sep, 2026

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.

Try It Yourself
redirect icon

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.
C++
#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;
}
Java
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();
        }
    }
}
Python
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)
C#
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();
        }
    }
}
JavaScript
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.

Comment