Given a string licensePlate
and an array of strings words
, you are to find the shortest completing word in words
.
A completing word is a word that contains all the letters in licensePlate
(ignoring case, digits, and spaces), with each letter occurring at least as many times in the word as it appears in licensePlate
.
You cannot reuse the same letter from licensePlate
more than it appears.
words
. If there are multiple, return the first one in the list.licensePlate
.
At first glance, the problem suggests checking every word in words
to see if it "completes" the licensePlate
.
The brute-force way would be to, for each word, count the letters and compare them to the required letters from licensePlate
.
However, this can be made more efficient. Instead of repeatedly extracting letters from licensePlate
, we can preprocess it once to get a frequency count of its required letters.
Then, for each word, we can count its letters and check if it covers all the required counts.
This shifts the problem from repeated string scanning to a more optimized frequency comparison, leveraging hash maps (dictionaries) for fast lookups.
licensePlate
words
licensePlate
, check if the word contains at least as many occurrences.Using a hash map or array for letter counts ensures that checking each word is efficient (O(1) per letter), making the overall approach much faster than naive character-by-character matching.
Input: licensePlate = "1s3 PSt"
, words = ["step", "steps", "stripe", "stepple"]
licensePlate
:
licensePlate
, scan the word to count matches.licensePlate
: O(L)
The key to solving the "Shortest Completing Word" problem efficiently is to preprocess the licensePlate
into a frequency table, then use letter counts to quickly check each word.
This avoids repeated scanning and leverages fast hash map or array lookups.
The solution is both straightforward and efficient, making it a great example of how preprocessing and data structures can simplify string-matching problems.
from collections import Counter
class Solution:
def shortestCompletingWord(self, licensePlate: str, words: list) -> str:
# Prepare frequency count of letters in licensePlate
license_count = Counter(
c.lower() for c in licensePlate if c.isalpha()
)
answer = None
for word in words:
word_count = Counter(word.lower())
if all(word_count[char] >= license_count[char] for char in license_count):
if answer is None or len(word) < len(answer):
answer = word
return answer
#include <vector>
#include <string>
#include <cctype>
#include <algorithm>
using namespace std;
class Solution {
public:
string shortestCompletingWord(string licensePlate, vector<string>& words) {
int licenseCount[26] = {0};
for (char c : licensePlate) {
if (isalpha(c)) {
licenseCount[tolower(c) - 'a']++;
}
}
string answer = "";
for (const string& word : words) {
int wordCount[26] = {0};
for (char c : word) {
wordCount[tolower(c) - 'a']++;
}
bool valid = true;
for (int i = 0; i < 26; ++i) {
if (licenseCount[i] > wordCount[i]) {
valid = false;
break;
}
}
if (valid && (answer.empty() || word.length() < answer.length())) {
answer = word;
}
}
return answer;
}
};
class Solution {
public String shortestCompletingWord(String licensePlate, String[] words) {
int[] licenseCount = new int[26];
for (char c : licensePlate.toCharArray()) {
if (Character.isLetter(c)) {
licenseCount[Character.toLowerCase(c) - 'a']++;
}
}
String answer = null;
for (String word : words) {
int[] wordCount = new int[26];
for (char c : word.toCharArray()) {
wordCount[Character.toLowerCase(c) - 'a']++;
}
boolean valid = true;
for (int i = 0; i < 26; i++) {
if (licenseCount[i] > wordCount[i]) {
valid = false;
break;
}
}
if (valid && (answer == null || word.length() < answer.length())) {
answer = word;
}
}
return answer;
}
}
/**
* @param {string} licensePlate
* @param {string[]} words
* @return {string}
*/
var shortestCompletingWord = function(licensePlate, words) {
const licenseCount = Array(26).fill(0);
for (let c of licensePlate) {
if (/[a-zA-Z]/.test(c)) {
licenseCount[c.toLowerCase().charCodeAt(0) - 97]++;
}
}
let answer = null;
for (let word of words) {
const wordCount = Array(26).fill(0);
for (let c of word) {
wordCount[c.toLowerCase().charCodeAt(0) - 97]++;
}
let valid = true;
for (let i = 0; i < 26; i++) {
if (licenseCount[i] > wordCount[i]) {
valid = false;
break;
}
}
if (valid && (answer === null || word.length < answer.length)) {
answer = word;
}
}
return answer;
};