A clear explanation of finding the longest dictionary word obtainable as a subsequence using two pointers and sorting rules.
Problem Restatement
We are given:
s
dictionaryWe may delete some characters from s.
We need to return the longest string in dictionary that can be formed as a subsequence of s.
If multiple answers have the same length, return the lexicographically smallest one.
If no dictionary word can be formed, return the empty string.
The official problem asks us to find the longest dictionary word obtainable by deleting characters from s. If several answers have the same length, choose the lexicographically smallest one. (leetcode.com)
Input and Output
| Item | Meaning |
|---|---|
| Input | String s, list of strings dictionary |
| Output | Best matching dictionary word |
| Subsequence rule | Delete characters without changing order |
| Tie-breaking | Lexicographically smallest among longest words |
Function shape:
class Solution:
def findLongestWord(
self,
s: str,
dictionary: List[str]
) -> str:
...Examples
Example 1:
s = "abpcplea"
dictionary = ["ale", "apple", "monkey", "plea"]Possible subsequences include:
| Word | Is subsequence? |
|---|---|
"ale" | Yes |
"apple" | Yes |
"monkey" | No |
"plea" | Yes |
The longest valid word is:
appleSo the answer is:
"apple"Example 2:
s = "abpcplea"
dictionary = ["a", "b", "c"]All words have length 1.
The lexicographically smallest is:
aSo the answer is:
"a"First Thought: Test Every Dictionary Word
For each dictionary word:
- Check whether it is a subsequence of
s - Keep the best valid candidate
This works because the dictionary size is manageable.
The main subproblem is efficient subsequence checking.
Key Insight
Use two pointers.
To check whether word w is a subsequence of s:
- Scan through
s - Advance a pointer in
wwhenever characters match - If all characters of
ware matched, thenwis a subsequence
Example:
s = "abpcplea"
w = "apple"Matching process:
s character | Needed character | Match? |
|---|---|---|
a | a | Yes |
b | p | No |
p | p | Yes |
c | p | No |
p | p | Yes |
l | l | Yes |
e | e | Yes |
All characters were matched.
So "apple" is a subsequence.
Choosing the Best Candidate
Suppose several words are valid subsequences.
We prefer:
- Larger length
- Lexicographically smaller if lengths are equal
So for candidate word, update the answer if:
len(word) > len(answer)or:
len(word) == len(answer)
and word < answerAlgorithm
Initialize:
answer = ""For each word in dictionary:
- Check whether it is a subsequence of
s - If valid:
- update
answerif the word is longer - or if same length but lexicographically smaller
- update
Return answer.
Correctness
The helper function correctly determines whether a dictionary word is a subsequence of s by scanning s left to right and matching characters in order.
A word is considered valid exactly when all of its characters can be matched in sequence inside s.
The algorithm checks every dictionary word, so every valid candidate is considered.
The update rule always keeps the best candidate according to the problem ordering:
- Prefer larger length
- For equal lengths, prefer lexicographically smaller words
Therefore, after processing the entire dictionary, answer is exactly the required output.
If no word is a subsequence of s, the answer remains the empty string, which is correct.
Complexity
Let:
| Symbol | Meaning |
|---|---|
n | Length of s |
d | Number of dictionary words |
m | Average dictionary word length |
| Metric | Value | Why |
|---|---|---|
| Time | O(d * n) worst case | Each subsequence check may scan s |
| Space | O(1) | Only pointers and counters |
More precisely, the total work is proportional to all subsequence checks.
Implementation
from typing import List
class Solution:
def findLongestWord(
self,
s: str,
dictionary: List[str]
) -> str:
def is_subsequence(word: str) -> bool:
i = 0
for ch in s:
if i < len(word) and word[i] == ch:
i += 1
return i == len(word)
answer = ""
for word in dictionary:
if is_subsequence(word):
if (
len(word) > len(answer)
or (
len(word) == len(answer)
and word < answer
)
):
answer = word
return answerCode Explanation
The helper function checks subsequences:
def is_subsequence(word: str) -> bool:i tracks how many characters of word have been matched:
i = 0Scan through s:
for ch in s:Advance the pointer when characters match:
if i < len(word) and word[i] == ch:
i += 1If all characters were matched:
return i == len(word)then the word is a subsequence.
Now process every dictionary word:
for word in dictionary:If it is valid:
if is_subsequence(word):update the answer if:
len(word) > len(answer)or if lengths tie and the new word is lexicographically smaller:
word < answerTesting
def test_find_longest_word():
s = Solution()
assert (
s.findLongestWord(
"abpcplea",
["ale", "apple", "monkey", "plea"]
)
== "apple"
)
assert (
s.findLongestWord(
"abpcplea",
["a", "b", "c"]
)
== "a"
)
assert (
s.findLongestWord(
"bab",
["ba", "ab", "a", "b"]
)
== "ab"
)
assert (
s.findLongestWord(
"abc",
["d", "e"]
)
== ""
)
assert (
s.findLongestWord(
"aaa",
["aa", "aaa", "aaaa"]
)
== "aaa"
)
print("all tests passed")Test meaning:
| Test | Why |
|---|---|
| Standard example | Longest valid subsequence |
| Same-length candidates | Lexicographic tie-breaking |
"ba" vs "ab" | Lexicographic comparison |
| No valid dictionary word | Empty string case |
| Word longer than source | Must reject impossible subsequence |