Find the shortest word that contains all required license plate letters using frequency counting.
Problem Restatement
We are given a string licensePlate and a list of words.
A word is called a completing word if it contains every letter from licensePlate.
When reading licensePlate:
- Ignore digits.
- Ignore spaces.
- Treat uppercase and lowercase letters as the same.
- If a letter appears multiple times, the word must contain it at least that many times.
We need to return the shortest completing word from words.
If there are multiple shortest completing words, return the one that appears first in words. The problem guarantees that an answer exists.
Input and Output
| Item | Meaning |
|---|---|
| Input | licensePlate and words |
| Output | The shortest completing word |
| Ignored characters | Digits and spaces in licensePlate |
| Case handling | Case-insensitive |
| Tie rule | Return the first shortest word in words |
Example function shape:
def shortestCompletingWord(
licensePlate: str,
words: list[str],
) -> str:
...Examples
Example 1
licensePlate = "1s3 PSt"
words = ["step", "steps", "stripe", "stepple"]After removing digits and spaces, and converting to lowercase, the required letters are:
s, p, s, tSo the required counts are:
| Letter | Count |
|---|---|
| s | 2 |
| p | 1 |
| t | 1 |
Check the words:
| Word | Completing? | Why |
|---|---|---|
"step" | No | Has only one s |
"steps" | Yes | Has s twice, p once, t once |
"stripe" | No | Has only one s |
"stepple" | No | Has only one s |
The answer is:
"steps"Example 2
licensePlate = "1s3 456"
words = ["looks", "pest", "stew", "show"]The required letters are:
sThe shortest words containing s are "pest" and "stew".
Both have length 4, but "pest" appears first.
The answer is:
"pest"First Thought: Compare Each Word
We can preprocess the license plate once.
For each word, count its letters and check whether it contains enough of every required letter.
Because there are only 26 lowercase English letters, this comparison is small and simple.
Key Insight
This is a frequency counting problem.
Build a required count array:
need[letter]Then for each candidate word, build:
have[letter]The word is completing if for every letter:
have[letter] >= need[letter]To preserve the tie rule, scan words in order and update the answer only when the valid word is strictly shorter than the current answer.
Algorithm
- Create an array
needof size26. - For each character in
licensePlate:- If it is a letter, convert it to lowercase.
- Increment its count in
need.
- Initialize
answer = None. - For each
wordinwords:- If
answerexists andlen(word) >= len(answer), skip it. - Count letters in
word. - If the word satisfies all required counts, set
answer = word.
- If
- Return
answer.
Correctness
The need array records exactly the letters required by the license plate, ignoring digits and spaces and treating letters case-insensitively.
For each word, the algorithm counts its letters and compares those counts with need. A word is accepted only if every required letter appears at least as many times as needed. This matches the definition of a completing word.
The algorithm scans words from left to right. It updates the answer only when it finds a completing word with strictly smaller length. Therefore, when two completing words have the same shortest length, the earlier one remains selected.
Since the problem guarantees that at least one completing word exists, the final answer is the shortest completing word with the required tie behavior.
Complexity
Let n = len(words) and let L be the maximum word length.
| Metric | Value | Why |
|---|---|---|
| Time | O(len(licensePlate) + n * L) | Count the license plate once, then count each candidate word |
| Space | O(1) | Count arrays have fixed size 26 |
The output string itself is not counted as extra working space.
Implementation
class Solution:
def shortestCompletingWord(
self,
licensePlate: str,
words: list[str],
) -> str:
need = [0] * 26
for ch in licensePlate:
if ch.isalpha():
index = ord(ch.lower()) - ord("a")
need[index] += 1
answer = None
for word in words:
if answer is not None and len(word) >= len(answer):
continue
have = [0] * 26
for ch in word:
index = ord(ch) - ord("a")
have[index] += 1
ok = True
for i in range(26):
if have[i] < need[i]:
ok = False
break
if ok:
answer = word
return answerCode Explanation
The required letter counts are stored here:
need = [0] * 26For every character in the license plate, we only keep letters:
if ch.isalpha():Then we convert to lowercase and count it:
index = ord(ch.lower()) - ord("a")
need[index] += 1For each word, we can skip it if it cannot improve the current answer:
if answer is not None and len(word) >= len(answer):
continueThis keeps the first word when there is a tie.
Then we count the word letters:
have = [0] * 26
for ch in word:
index = ord(ch) - ord("a")
have[index] += 1Finally, compare the two arrays:
for i in range(26):
if have[i] < need[i]:
ok = False
breakIf every required count is satisfied, this word becomes the best answer so far.
Testing
def run_tests():
s = Solution()
assert s.shortestCompletingWord(
"1s3 PSt",
["step", "steps", "stripe", "stepple"],
) == "steps"
assert s.shortestCompletingWord(
"1s3 456",
["looks", "pest", "stew", "show"],
) == "pest"
assert s.shortestCompletingWord(
"Ah71752",
["suggest", "letter", "of", "husband", "easy", "education", "drug"],
) == "husband"
assert s.shortestCompletingWord(
"OgEu755",
["enough", "these", "play", "wide", "wonder", "box", "arrive", "money", "tax", "thus"],
) == "enough"
assert s.shortestCompletingWord(
"aBc 12c",
["abccdef", "cbca", "caaacab"],
) == "cbca"
print("all tests passed")
run_tests()| Test | Why |
|---|---|
"1s3 PSt" | Requires repeated s and ignores case |
"1s3 456" | Tie chooses earlier shortest word |
"Ah71752" | Mixed digits and uppercase letters |
"OgEu755" | Multiple required letters |
"aBc 12c" | Repeated c, shortest valid word wins |