Longest Matching in dictionary by deleting some characters of given string

Last Updated : 22 Aug, 2026

Given a lowercase string s and a dictionary d[] containing lowercase words, find the longest word in the dictionary that can be obtained by deleting some characters from s without changing the order of the remaining characters. If multiple words have the same maximum length, return the lexicographically smallest one. If no valid word exists, return an empty string.

Examples: 

Input: d = ["ale", "apple", "monkey", "plea"], s = "abpcplea"
Output: "apple" 
Explanation: After deleting "b", "c", "a" s became "apple" which is present in d.

Input: d = ["a", "b", "c"], s = "abpcplea"
Output: "a"
Explanation: After deleting "b", "p", "c", "p", "l", "e", "a" s became "a" which is present in d.

Try It Yourself
redirect icon

[Naive Approach] Check Every Dictionary Word as a Subsequence - O(n * |s|) Time O(1) Space

The idea is to traverse every word in the dictionary and check whether it is a subsequence of the given string s using two pointers.

If a word is a valid subsequence, compare it with the current answer. Update the answer if the word has a greater length, or if lengths are equal and the word is lexicographically smaller.

C++
#include <bits/stdc++.h>
using namespace std;

bool isSubsequence(string &s, string &word)
{
    int i = 0, j = 0;

    while (i < s.size() && j < word.size())
    {
        if (s[i] == word[j])
        {
            j++;
        }
        i++;
    }

    return j == word.size();
}

string findLongestWord(string &s, vector<string> &d)
{
    string res = "";

    for (string &word : d)
    {

        // Check if current word is a subsequence of s
        if (isSubsequence(s, word))
        {

            // Update result if longer word is found
            if (word.size() > res.size())
            {
                res = word;
            }

            // If lengths are same, keep lexicographically smaller word
            else if (word.size() == res.size() && word < res)
            {
                res = word;
            }
        }
    }

    return res;
}

// Driver Code
int main()
{
    string s = "abpcplea";
    vector<string> d = {"ale", "apple", "monkey", "plea"};

    cout << findLongestWord(s, d);

    return 0;
}
Java
import java.util.*;

public class GFG {

    public static boolean isSubsequence(String s,
                                        String word)
    {
        int i = 0, j = 0;

        while (i < s.length() && j < word.length()) {
            if (s.charAt(i) == word.charAt(j)) {
                j++;
            }
            i++;
        }

        return j == word.length();
    }

    public static String findLongestWord(String s,
                                         List<String> d)
    {
        String res = "";

        for (String word : d) {

            // Check if current word is a subsequence of s
            if (isSubsequence(s, word)) {

                // Update result if longer word is found
                if (word.length() > res.length()) {
                    res = word;
                }

                // If lengths are same, keep
                // lexicographically smaller word
                else if (word.length() == res.length()
                         && word.compareTo(res) < 0) {
                    res = word;
                }
            }
        }

        return res;
    }

    public static void main(String[] args)
    {
        String s = "abpcplea";

        List<String> d = Arrays.asList("ale", "apple",
                                       "monkey", "plea");

        System.out.println(findLongestWord(s, d));
    }
}
Python
def isSubsequence(s, word):
    i = 0
    j = 0

    while i < len(s) and j < len(word):
        if s[i] == word[j]:
            j += 1
        i += 1

    return j == len(word)


def findLongestWord(s, d):
    res = ""

    for word in d:

        # Check if current word is a subsequence of s
        if isSubsequence(s, word):

            # Update result if longer word is found
            if len(word) > len(res):
                res = word

            # If lengths are same, keep lexicographically smaller word
            elif len(word) == len(res) and word < res:
                res = word

    return res


# Driver Code
if __name__ == "__main__":
    s = "abpcplea"
    d = ["ale", "apple", "monkey", "plea"]

    print(findLongestWord(s, d))
C#
using System;
using System.Collections.Generic;

public class GfG {
    public static bool IsSubsequence(string s, string word)
    {
        int i = 0, j = 0;

        while (i < s.Length && j < word.Length) {
            if (s[i] == word[j]) {
                j++;
            }
            i++;
        }

        return j == word.Length;
    }

    public static string findLongestWord(string s,
                                         List<string> d)
    {
        string res = "";

        foreach(string word in d)
        {

            // Check if current word is a subsequence of s
            if (IsSubsequence(s, word)) {

                // Update result if longer word is found
                if (word.Length > res.Length) {
                    res = word;
                }

                // If lengths are same, keep
                // lexicographically smaller word
                else if (word.Length == res.Length
                         && string.Compare(word, res) < 0) {
                    res = word;
                }
            }
        }

        return res;
    }

    public static void Main()
    {
        string s = "abpcplea";
        List<string> d
            = new List<string>{ "ale", "apple", "monkey",
                                "plea" };

        Console.WriteLine(findLongestWord(s, d));
    }
}
JavaScript
function isSubsequence(s, word)
{
    let i = 0, j = 0;

    while (i < s.length && j < word.length) {
        if (s[i] === word[j]) {
            j++;
        }
        i++;
    }

    return j === word.length;
}

function findLongestWord(s, d)
{
    let res = "";

    for (let word of d) {

        // Check if current word is a subsequence of s
        if (isSubsequence(s, word)) {

            // Update result if longer word is found
            if (word.length > res.length) {
                res = word;
            }

            // If lengths are same, keep lexicographically
            // smaller word
            else if (word.length === res.length
                     && word < res) {
                res = word;
            }
        }
    }

    return res;
}

// Driver Code
let s = "abpcplea";
let d = [ "ale", "apple", "monkey", "plea" ];

console.log(findLongestWord(s, d));

Output
apple

Time Complexity: O(n * |s|)
Auxiliary Space: O(1)

[Expected Approach] Index Mapping + Binary Search - O(|s| + n * maxWordLen * log |s|) Time O(|s|) Space

The idea is to first store all positions of every character present in s in an array of arrays. Now for each dictionary word, try to match its characters in order.

Using binary search, find the next occurrence of each character after the previously matched position. If all characters of a word can be matched, then it is a valid subsequence.

Among all valid words, choose the longest one, and if multiple words have the same length, choose the lexicographically smallest.

Let us understand with example:
Input: d = ["ale", "apple", "monkey", "plea"], s = "abpcplea"

  • For s = "abpcplea", store the indices of each character in separate lists. Initially, res = "".
  • Check "ale" and use binary search to find 'a' at index 0, 'l' at index 5, and 'e' at index 6. Hence, "ale" is a valid subsequence and res = "ale".
  • Next, for "apple", find 'a' -> 0, 'p' -> 2, 'p' -> 4, 'l' -> 5, and 'e' -> 6. It is also a valid subsequence and is longer than "ale", so update res = "apple".
  • The word "monkey" is not a subsequence since 'm' does not occur in s, while "plea" is shorter than the current result "apple", so it is skipped. Therefore, the final answer is "apple".
C++
#include <bits/stdc++.h>
using namespace std;

// Returns true if 'word' is a subsequence of string 's'
bool isSubsequence(const string &word, const vector<vector<int>> &pos)
{

    int prevIndex = -1;

    for (char ch : word)
    {

        // All positions where character 'ch' occurs in s
        const vector<int> &indices = pos[ch - 'a'];

        // Find first occurrence of ch after prevIndex
        auto it = upper_bound(indices.begin(), indices.end(), prevIndex);

        // No valid next position found
        if (it == indices.end())
        {
            return false;
        }

        // Update previously matched index
        prevIndex = *it;
    }

    return true;
}

string findLongestWord(string &s, vector<string> &d)
{

    // Store positions of every lowercase character in s
    vector<vector<int>> pos(26);

    for (int i = 0; i < s.size(); i++)
    {
        pos[s[i] - 'a'].push_back(i);
    }

    string res = "";

    for (const string &word : d)
    {

        // Skip smaller words directly
        if (word.size() < res.size())
        {
            continue;
        }

        // Check whether word is subsequence of s
        if (isSubsequence(word, pos))
        {

            // Prefer longer word
            // If same length, prefer lexicographically smaller word
            if (word.size() > res.size() || (word.size() == res.size() && word < res))
            {
                res = word;
            }
        }
    }

    return res;
}

// Driver Code
int main()
{
    string s = "abpcplea";
    vector<string> d = {"ale", "apple", "monkey", "plea"};

    cout << findLongestWord(s, d);

    return 0;
}
Java
import java.util.ArrayList;
import java.util.List;

public class GFG {

    // Returns true if 'word' is a subsequence of string's'
    public static boolean
    isSubsequence(String word, List<List<Integer> > pos)
    {

        int prevIndex = -1;

        for (char ch : word.toCharArray()) {

            // All positions where character 'ch' occurs in
            // s
            List<Integer> indices = pos.get(ch - 'a');

            // Find first occurrence of ch after prevIndex
            int it = -1;
            for (int i = 0; i < indices.size(); i++) {
                if (indices.get(i) > prevIndex) {
                    it = indices.get(i);
                    break;
                }
            }

            // No valid next position found
            if (it == -1) {
                return false;
            }

            // Update previously matched index
            prevIndex = it;
        }

        return true;
    }

    public static String findLongestWord(String s,
                                         List<String> d)
    {

        // Store positions of every lowercase character in s
        List<List<Integer> > pos = new ArrayList<>();
        for (int i = 0; i < 26; i++) {
            pos.add(new ArrayList<>());
        }

        for (int i = 0; i < s.length(); i++) {
            pos.get(s.charAt(i) - 'a').add(i);
        }

        String res = "";

        for (String word : d) {

            // Skip smaller words directly
            if (word.length() < res.length()) {
                continue;
            }

            // Check whether word is subsequence of s
            if (isSubsequence(word, pos)) {

                // Prefer longer word
                // If same length, prefer lexicographically
                // smaller word
                if (word.length() > res.length()
                    || (word.length() == res.length()
                        && word.compareTo(res) < 0)) {

                    res = word;
                }
            }
        }

        return res;
    }

    // Driver Code
    public static void main(String[] args)
    {
        String s = "abpcplea";
        List<String> d
            = List.of("ale", "apple", "monkey", "plea");

        System.out.println(findLongestWord(s, d));
    }
}
Python
from bisect import bisect_right


# Returns True if 'word' is a subsequence of string 's'
def isSubsequence(word, pos):

    prevIndex = -1

    for ch in word:

        # All positions where character 'ch' occurs in s
        indices = pos[ord(ch) - ord('a')]

        # Find first occurrence of ch after prevIndex
        idx = bisect_right(indices, prevIndex)

        # No valid next position found
        if idx == len(indices):
            return False

        # Update previously matched index
        prevIndex = indices[idx]

    return True


def findLongestWord(s, d):

    # Store positions of every lowercase character in s
    pos = [[] for _ in range(26)]

    for i in range(len(s)):
        pos[ord(s[i]) - ord('a')].append(i)

    res = ""

    for word in d:

        # Skip smaller words directly
        if len(word) < len(res):
            continue

        # Check whether word is subsequence of s
        if isSubsequence(word, pos):

            # Prefer longer word
            # If same length, prefer lexicographically smaller word
            if (len(word) > len(res) or
                    (len(word) == len(res) and word < res)):

                res = word

    return res


# Driver Code
if __name__ == "__main__":
    s = "abpcplea"
    d = ["ale", "apple", "monkey", "plea"]

    print(findLongestWord(s, d))
C#
using System;
using System.Collections.Generic;

class GFG {
    // Returns true if 'word' is a subsequence of string's'
    public static bool IsSubsequence(string word,
                                     List<List<int> > pos)
    {
        int prevIndex = -1;

        foreach(char ch in word)
        {
            // All positions where character 'ch' occurs in
            // s
            List<int> indices = pos[ch - 'a'];

            // Find first occurrence of ch after prevIndex
            int it = -1;
            foreach(int index in indices)
            {
                if (index > prevIndex) {
                    it = index;
                    break;
                }
            }

            // No valid next position found
            if (it == -1) {
                return false;
            }

            // Update previously matched index
            prevIndex = it;
        }

        return true;
    }

    public static string findLongestWord(string s,
                                         List<string> d)
    {
        // Store positions of every lowercase character in s
        List<List<int> > pos = new List<List<int> >();
        for (int i = 0; i < 26; i++) {
            pos.Add(new List<int>());
        }

        for (int i = 0; i < s.Length; i++) {
            pos[s[i] - 'a'].Add(i);
        }

        string res = "";

        foreach(string word in d)
        {
            // Skip smaller words directly
            if (word.Length < res.Length) {
                continue;
            }

            // Check whether word is subsequence of s
            if (IsSubsequence(word, pos)) {
                // Prefer longer word
                // If same length, prefer lexicographically
                // smaller word
                if (word.Length > res.Length
                    || (word.Length == res.Length
                        && string.Compare(word, res) < 0)) {
                    res = word;
                }
            }
        }

        return res;
    }

    // Driver Code
    public static void Main(string[] args)
    {
        string s = "abpcplea";
        List<string> d
            = new List<string>{ "ale", "apple", "monkey",
                                "plea" };

        Console.WriteLine(findLongestWord(s, d));
    }
}
JavaScript
// Returns true if 'word' is a subsequence of string's'
function isSubsequence(word, pos)
{

    let prevIndex = -1;

    for (let ch of word) {

        // All positions where character 'ch' occurs in s
        let indices
            = pos[ch.charCodeAt(0) - "a".charCodeAt(0)];

        // Find first occurrence of ch after prevIndex
        let it = -1;
        for (let index of indices) {
            if (index > prevIndex) {
                it = index;
                break;
            }
        }

        // No valid next position found
        if (it === -1) {
            return false;
        }

        // Update previously matched index
        prevIndex = it;
    }

    return true;
}

function findLongestWord(s, d)
{

    // Store positions of every lowercase character in s
    let pos = new Array(26).fill().map(() => []);

    for (let i = 0; i < s.length; i++) {
        pos[s.charCodeAt(i) - "a".charCodeAt(0)].push(i);
    }

    let res = "";

    for (let word of d) {

        // Skip smaller words directly
        if (word.length < res.length) {
            continue;
        }

        // Check whether word is subsequence of s
        if (isSubsequence(word, pos)) {

            // Prefer longer word
            // If same length, prefer lexicographically
            // smaller word
            if (word.length > res.length
                || (word.length === res.length
                    && word < res)) {
                res = word;
            }
        }
    }

    return res;
}

// Driver Code
let s = "abpcplea";
let d = [ "ale", "apple", "monkey", "plea" ];

console.log(findLongestWord(s, d));

Output
apple

Time Complexity: O(|s| + n * maxWordLen * log |s|)
Auxiliary Space: O(|s|)

Comment