The Unique Word Abbreviation problem asks you to design a data structure that can efficiently determine if a given word's abbreviation is unique within a provided dictionary of words.
The abbreviation of a word is defined as follows: For a word of length n
, its abbreviation is the concatenation of its first letter, the number n - 2
(representing the number of characters between the first and last letter), and its last letter. If the word length is less than or equal to 2, the abbreviation is just the word itself.
You are given a list of words dictionary
and must implement two operations:
ValidWordAbbr(dictionary)
: Initializes the data structure with the given dictionary.
isUnique(word)
: Returns true
if the abbreviation of word
is unique in the dictionary; otherwise, returns false
.
Constraints:
At first glance, the problem seems to require checking each word in the dictionary for every isUnique
call, comparing abbreviations. This brute-force approach would be slow, especially for large dictionaries or many queries.
To optimize, we realize that abbreviations can be precomputed and mapped to their corresponding words. If we store, for each abbreviation, the set of words in the dictionary that share it, we can quickly answer uniqueness queries:
We'll solve the problem using a hash map (dictionary) to map abbreviations to the set of words in the dictionary that have that abbreviation.
isUnique(word)
, compute the word's abbreviation and check:
true
.true
.false
.Let's walk through an example:
Dictionary: ["deer","door","cake","card"]
Abbreviations:
"deer"
→ "d2r"
"door"
→ "d2r"
"cake"
→ "c2e"
"card"
→ "c2d"
isUnique("dear"):
"d2r"
"d2r"
maps to {"deer", "door"}false
"c2t"
true
"c2e"
true
Brute-force Approach:
isUnique
call scans the entire dictionary: O(N) per query, where N is the number of words.isUnique
call, as hash map lookups and set length checks are constant time.The optimized approach is much faster for repeated queries.
The Unique Word Abbreviation problem is efficiently solved by precomputing and mapping word abbreviations to sets of words using a hash map. This allows us to answer uniqueness queries in constant time, leveraging the power of hash-based data structures for fast lookups. The main insight is to shift from repeated scanning to smart preprocessing and O(1) queries, making the solution both elegant and scalable.
class ValidWordAbbr:
def __init__(self, dictionary):
self.abbr_dict = {}
self.dictionary = set(dictionary)
for word in self.dictionary:
abbr = self._abbr(word)
if abbr not in self.abbr_dict:
self.abbr_dict[abbr] = set()
self.abbr_dict[abbr].add(word)
def _abbr(self, word):
if len(word) <= 2:
return word
return word[0] + str(len(word) - 2) + word[-1]
def isUnique(self, word):
abbr = self._abbr(word)
if abbr not in self.abbr_dict:
return True
words = self.abbr_dict[abbr]
return words == {word}
class ValidWordAbbr {
unordered_map<string, unordered_set<string>> abbr_map;
public:
ValidWordAbbr(vector<string>& dictionary) {
unordered_set<string> dict(dictionary.begin(), dictionary.end());
for (const string& word : dict) {
string abbr = getAbbr(word);
abbr_map[abbr].insert(word);
}
}
string getAbbr(const string& word) {
if (word.length() <= 2) return word;
return word.front() + to_string(word.length() - 2) + word.back();
}
bool isUnique(string word) {
string abbr = getAbbr(word);
if (abbr_map.find(abbr) == abbr_map.end()) return true;
const auto& words = abbr_map[abbr];
return words.size() == 1 && words.count(word);
}
};
import java.util.*;
class ValidWordAbbr {
private Map<String, Set<String>> abbrMap;
public ValidWordAbbr(String[] dictionary) {
abbrMap = new HashMap<>();
Set<String> dict = new HashSet<>(Arrays.asList(dictionary));
for (String word : dict) {
String abbr = abbr(word);
abbrMap.computeIfAbsent(abbr, k -> new HashSet<>()).add(word);
}
}
private String abbr(String word) {
if (word.length() <= 2) return word;
return "" + word.charAt(0) + (word.length() - 2) + word.charAt(word.length() - 1);
}
public boolean isUnique(String word) {
String abbr = abbr(word);
if (!abbrMap.containsKey(abbr)) return true;
Set<String> words = abbrMap.get(abbr);
return words.size() == 1 && words.contains(word);
}
}
class ValidWordAbbr {
constructor(dictionary) {
this.abbrMap = new Map();
this.dictionary = new Set(dictionary);
for (let word of this.dictionary) {
const abbr = this._abbr(word);
if (!this.abbrMap.has(abbr)) {
this.abbrMap.set(abbr, new Set());
}
this.abbrMap.get(abbr).add(word);
}
}
_abbr(word) {
if (word.length <= 2) return word;
return word[0] + (word.length - 2) + word[word.length - 1];
}
isUnique(word) {
const abbr = this._abbr(word);
if (!this.abbrMap.has(abbr)) return true;
const words = this.abbrMap.get(abbr);
return words.size === 1 && words.has(word);
}
}