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.
Table of Content
[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
susing 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.
#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;
}
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));
}
}
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))
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));
}
}
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".
#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;
}
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));
}
}
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))
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));
}
}
// 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|)