288. Unique Word Abbreviation 🔒
Problem Description
This problem asks you to implement a class that checks if a word's abbreviation is unique within a dictionary.
Abbreviation Rules:
- For words with 3 or more characters: the abbreviation is the first letter + the count of middle characters + the last letter
- Example:
"dog"
becomes"d1g"
(1 character between 'd' and 'g') - Example:
"internationalization"
becomes"i18n"
(18 characters between 'i' and 'n')
- Example:
- For words with exactly 2 characters: the word is its own abbreviation
- Example:
"it"
remains"it"
- Example:
Class Implementation:
You need to implement the ValidWordAbbr
class with two methods:
__init__(dictionary)
: Initialize the object with an array of wordsisUnique(word)
: Returnstrue
if the word is unique,false
otherwise
Uniqueness Definition:
A word is considered unique if ONE of these conditions is true:
- No word in the dictionary has the same abbreviation as the given word
- All words in the dictionary that share the same abbreviation are exactly the same as the given word
For example, if the dictionary contains ["deer", "door", "cake", "card"]
:
isUnique("dear")
returnsfalse
because"deer"
from the dictionary has the same abbreviation"d2r"
isUnique("cart")
returnstrue
because although"card"
has abbreviation"c2d"
,"cart"
has abbreviation"c2t"
which doesn't matchisUnique("cane")
returnsfalse
because both"cake"
and"cane"
have abbreviation"c2e"
but they are different wordsisUnique("make")
returnstrue
because no word in dictionary has abbreviation"m2e"
isUnique("cake")
returnstrue
because although"cake"
in dictionary has abbreviation"c2e"
, it's the same word
Intuition
The key insight is that we need to quickly check if a word's abbreviation conflicts with words in our dictionary. Since we'll be making multiple queries, we should preprocess the dictionary to make lookups efficient.
Think about what we're really checking: given a word, we want to know if its abbreviation is "unique" according to the specific rules. The word is unique if either:
- No dictionary word shares its abbreviation, OR
- All dictionary words that share its abbreviation are identical to the query word itself
This suggests we should group dictionary words by their abbreviations. A hash table is perfect for this - we can use the abbreviation as the key and store all words with that abbreviation as the value.
But what data structure should we use for the value? Since we need to:
- Check if all words with a given abbreviation are the same
- Avoid duplicate entries if the same word appears multiple times in the dictionary
A set is ideal. It automatically handles duplicates and makes it easy to check if all elements are identical to our query word.
During initialization, we compute each word's abbreviation and add it to our hash table. For the abbreviation function, we handle the special case where words with less than 3 characters are their own abbreviation.
When checking if a word is unique, we:
- Compute its abbreviation
- If that abbreviation isn't in our hash table, the word is unique
- If it is in our hash table, we check if all words in that set are identical to our query word
This approach gives us O(1)
average time complexity for lookups after an O(n)
preprocessing step, where n
is the size of the dictionary.
Solution Approach
We implement the solution using a hash table with sets as values to efficiently group words by their abbreviations.
Helper Function - abbr(s)
:
First, we create a helper function to compute abbreviations:
- If the word length is less than 3, return the word itself
- Otherwise, return
s[0] + str(len(s) - 2) + s[-1]
Initialization - __init__(dictionary)
:
- Create a
defaultdict(set)
calledd
to store our mapping - Iterate through each word
s
in the dictionary - Calculate the abbreviation using
self.abbr(s)
- Add the word to the set at
d[abbreviation]
Using a defaultdict(set)
ensures:
- No key errors when accessing non-existent abbreviations
- Automatic deduplication if the same word appears multiple times
- Easy grouping of all words with the same abbreviation
Uniqueness Check - isUnique(word)
:
- Calculate the abbreviation of the query word:
s = self.abbr(word)
- Check if this abbreviation exists in our hash table
- Return
true
if either:- The abbreviation
s
is not ind
(no conflicts), OR - All words in
d[s]
are identical toword
(checked usingall(word == t for t in self.d[s])
)
- The abbreviation
The expression all(word == t for t in self.d[s])
efficiently verifies that every word in the set with abbreviation s
is exactly the same as our query word. This handles the case where the word itself is in the dictionary.
Time Complexity:
- Initialization:
O(n)
wheren
is the dictionary size isUnique
:O(m)
wherem
is the number of words with the same abbreviation (usually small)
Space Complexity: O(n)
to store the dictionary words in the hash table
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example with dictionary ["deer", "door", "cake", "card"]
.
Step 1: Initialization
We create our hash table d
and process each word:
-
"deer"
→ abbreviation:"d2r"
(d + 2 middle chars + r)d["d2r"] = {"deer"}
-
"door"
→ abbreviation:"d2r"
(d + 2 middle chars + r)d["d2r"] = {"deer", "door"}
-
"cake"
→ abbreviation:"c2e"
(c + 2 middle chars + e)d["c2e"] = {"cake"}
-
"card"
→ abbreviation:"c2d"
(c + 2 middle chars + d)d["c2d"] = {"card"}
Final hash table:
d = { "d2r": {"deer", "door"}, "c2e": {"cake"}, "c2d": {"card"} }
Step 2: Query Examples
Query 1: isUnique("dear")
- Abbreviation of
"dear"
="d2r"
- Check: Is
"d2r"
ind
? Yes, it maps to{"deer", "door"}
- Check: Are all words in
{"deer", "door"}
equal to"dear"
? No - Return:
false
Query 2: isUnique("cart")
- Abbreviation of
"cart"
="c2t"
- Check: Is
"c2t"
ind
? No - Return:
true
(no conflicts)
Query 3: isUnique("cake")
- Abbreviation of
"cake"
="c2e"
- Check: Is
"c2e"
ind
? Yes, it maps to{"cake"}
- Check: Are all words in
{"cake"}
equal to"cake"
? Yes - Return:
true
(all words with this abbreviation are identical to query)
Query 4: isUnique("door")
- Abbreviation of
"door"
="d2r"
- Check: Is
"d2r"
ind
? Yes, it maps to{"deer", "door"}
- Check: Are all words in
{"deer", "door"}
equal to"door"
? No ("deer"
≠"door"
) - Return:
false
This walkthrough demonstrates how the hash table efficiently groups words by abbreviation and how the uniqueness check correctly handles both cases: when no conflicts exist and when all conflicting words are identical to the query.
Solution Implementation
1from collections import defaultdict
2from typing import List
3
4class ValidWordAbbr:
5 def __init__(self, dictionary: List[str]):
6 """
7 Initialize the data structure with a dictionary of words.
8 Store words grouped by their abbreviation.
9
10 Args:
11 dictionary: List of words to initialize the data structure
12 """
13 # Dictionary to store sets of words grouped by their abbreviation
14 self.abbreviation_map = defaultdict(set)
15
16 # Build the abbreviation map from the dictionary
17 for word in dictionary:
18 abbreviation = self._get_abbreviation(word)
19 self.abbreviation_map[abbreviation].add(word)
20
21 def isUnique(self, word: str) -> bool:
22 """
23 Check if a word's abbreviation is unique in the dictionary.
24 A word's abbreviation is unique if:
25 1. No word in the dictionary has the same abbreviation, OR
26 2. All words with the same abbreviation are the same as the input word
27
28 Args:
29 word: The word to check for uniqueness
30
31 Returns:
32 True if the word's abbreviation is unique, False otherwise
33 """
34 abbreviation = self._get_abbreviation(word)
35
36 # Check if abbreviation doesn't exist or all words with this abbreviation are the same as input
37 return (abbreviation not in self.abbreviation_map or
38 all(existing_word == word for existing_word in self.abbreviation_map[abbreviation]))
39
40 def _get_abbreviation(self, word: str) -> str:
41 """
42 Generate abbreviation for a word.
43 Rules:
44 - If word length < 3, the abbreviation is the word itself
45 - Otherwise, abbreviation is: first_letter + count_of_middle_letters + last_letter
46
47 Args:
48 word: The word to abbreviate
49
50 Returns:
51 The abbreviation of the word
52 """
53 if len(word) < 3:
54 return word
55
56 # Create abbreviation: first char + middle count + last char
57 middle_count = len(word) - 2
58 return f"{word[0]}{middle_count}{word[-1]}"
59
60
61# Your ValidWordAbbr object will be instantiated and called as such:
62# obj = ValidWordAbbr(dictionary)
63# param_1 = obj.isUnique(word)
64
1class ValidWordAbbr {
2 // Map to store abbreviation as key and set of original words as value
3 private Map<String, Set<String>> abbreviationMap = new HashMap<>();
4
5 /**
6 * Constructor to initialize the data structure with a dictionary
7 * @param dictionary Array of words to be stored
8 */
9 public ValidWordAbbr(String[] dictionary) {
10 // Process each word in the dictionary
11 for (String word : dictionary) {
12 // Get the abbreviation of the word
13 String abbreviation = getAbbreviation(word);
14 // Add the word to the set corresponding to its abbreviation
15 // Create a new HashSet if the abbreviation doesn't exist yet
16 abbreviationMap.computeIfAbsent(abbreviation, key -> new HashSet<>()).add(word);
17 }
18 }
19
20 /**
21 * Check if a word's abbreviation is unique in the dictionary
22 * A word's abbreviation is unique if:
23 * 1. No word in the dictionary has the same abbreviation, OR
24 * 2. The only word with this abbreviation is the word itself
25 * @param word The word to check
26 * @return true if the abbreviation is unique, false otherwise
27 */
28 public boolean isUnique(String word) {
29 String abbreviation = getAbbreviation(word);
30 Set<String> wordsWithSameAbbreviation = abbreviationMap.get(abbreviation);
31
32 // Return true if no words have this abbreviation, or
33 // if only one word has this abbreviation and it's the same as the input word
34 return wordsWithSameAbbreviation == null ||
35 (wordsWithSameAbbreviation.size() == 1 && wordsWithSameAbbreviation.contains(word));
36 }
37
38 /**
39 * Generate abbreviation for a word
40 * Rules:
41 * - If word length < 3, return the word itself
42 * - Otherwise, return first letter + (length - 2) + last letter
43 * @param word The word to abbreviate
44 * @return The abbreviation of the word
45 */
46 private String getAbbreviation(String word) {
47 int length = word.length();
48 if (length < 3) {
49 return word;
50 }
51 // Concatenate first character, middle count, and last character
52 return word.substring(0, 1) + (length - 2) + word.substring(length - 1);
53 }
54}
55
56/**
57 * Your ValidWordAbbr object will be instantiated and called as such:
58 * ValidWordAbbr obj = new ValidWordAbbr(dictionary);
59 * boolean param_1 = obj.isUnique(word);
60 */
61
1class ValidWordAbbr {
2public:
3 /**
4 * Constructor: Initializes the data structure with the given dictionary.
5 * Groups words by their abbreviations.
6 * @param dictionary: Vector of words to preprocess
7 */
8 ValidWordAbbr(vector<string>& dictionary) {
9 for (auto& word : dictionary) {
10 // Store each word grouped by its abbreviation
11 abbreviationMap[getAbbreviation(word)].insert(word);
12 }
13 }
14
15 /**
16 * Checks if a word's abbreviation is unique in the dictionary.
17 * A word's abbreviation is unique if:
18 * 1. No word in the dictionary has the same abbreviation, OR
19 * 2. The only word(s) with this abbreviation is the word itself
20 * @param word: The word to check
21 * @return: True if the abbreviation is unique, false otherwise
22 */
23 bool isUnique(string word) {
24 string abbreviation = getAbbreviation(word);
25
26 // Check if abbreviation doesn't exist OR
27 // exists with only one unique word which is the query word itself
28 return !abbreviationMap.count(abbreviation) ||
29 (abbreviationMap[abbreviation].size() == 1 &&
30 abbreviationMap[abbreviation].count(word));
31 }
32
33private:
34 // Maps abbreviation to set of original words that have this abbreviation
35 unordered_map<string, unordered_set<string>> abbreviationMap;
36
37 /**
38 * Generates abbreviation for a word.
39 * Rules:
40 * - If word length < 3: return the word as is
41 * - Otherwise: first_letter + (length-2) + last_letter
42 * @param word: The word to abbreviate
43 * @return: The abbreviation string
44 */
45 string getAbbreviation(string& word) {
46 int length = word.size();
47
48 if (length < 3) {
49 return word;
50 }
51
52 // Format: first letter + middle count + last letter
53 return word.substr(0, 1) + to_string(length - 2) + word.substr(length - 1, 1);
54 }
55};
56
57/**
58 * Your ValidWordAbbr object will be instantiated and called as such:
59 * ValidWordAbbr* obj = new ValidWordAbbr(dictionary);
60 * bool param_1 = obj->isUnique(word);
61 */
62
1// Map to store abbreviations and their corresponding words
2// Key: abbreviation string, Value: Set of original words
3let abbreviationMap: Map<string, Set<string>> = new Map();
4
5/**
6 * Initialize the abbreviation map with a dictionary of words
7 * @param dictionary - Array of words to process
8 */
9function ValidWordAbbr(dictionary: string[]): void {
10 // Clear the map for fresh initialization
11 abbreviationMap = new Map();
12
13 // Process each word in the dictionary
14 for (const word of dictionary) {
15 // Generate abbreviation for the current word
16 const abbreviation = abbr(word);
17
18 // Initialize set if abbreviation doesn't exist
19 if (!abbreviationMap.has(abbreviation)) {
20 abbreviationMap.set(abbreviation, new Set());
21 }
22
23 // Add the word to the set for this abbreviation
24 abbreviationMap.get(abbreviation)!.add(word);
25 }
26}
27
28/**
29 * Check if a word's abbreviation is unique
30 * A word's abbreviation is unique if:
31 * 1. No other word in the dictionary has the same abbreviation, OR
32 * 2. The only word(s) with the same abbreviation is the word itself
33 * @param word - The word to check
34 * @returns True if the abbreviation is unique, false otherwise
35 */
36function isUnique(word: string): boolean {
37 // Get the set of words with the same abbreviation
38 const wordsWithSameAbbr = abbreviationMap.get(abbr(word));
39
40 // Check if abbreviation doesn't exist or only contains the word itself
41 return wordsWithSameAbbr === undefined ||
42 (wordsWithSameAbbr.size === 1 && wordsWithSameAbbr.has(word));
43}
44
45/**
46 * Generate abbreviation for a word
47 * Rules:
48 * - If word length < 3, return the word as is
49 * - Otherwise, return first letter + (length - 2) + last letter
50 * @param word - The word to abbreviate
51 * @returns The abbreviation string
52 */
53function abbr(word: string): string {
54 const length = word.length;
55
56 // Words shorter than 3 characters remain unchanged
57 if (length < 3) {
58 return word;
59 }
60
61 // Format: first letter + middle count + last letter
62 return word[0] + (length - 2) + word[length - 1];
63}
64
Time and Space Complexity
Time Complexity:
-
Initialization (
__init__
):O(n)
wheren
is the total number of words in the dictionary. We iterate through each word once, and for each word, we compute its abbreviation inO(1)
time (accessing first and last characters and calculating length) and add it to a set inO(1)
average time. -
isUnique
method:O(m)
wherem
is the number of words in the setself.d[s]
. In the worst case, we need to check all words in the set to verify if they all equal the input word. However, in practice,m
is typically small, making this close toO(1)
for most cases. -
abbr
method:O(1)
as it only accesses the first and last characters of the string and performs basic arithmetic operations.
Space Complexity:
O(n)
wheren
is the total number of unique words in the dictionary. The hash table stores at mostn
words distributed across different abbreviation keys. Each abbreviation key maps to a set of words, and the total number of words stored across all sets is exactlyn
.
Common Pitfalls
1. Not Handling Duplicate Words in Dictionary
The Pitfall:
Many implementations fail to account for duplicate words in the input dictionary. If the dictionary contains ["dog", "dog"]
, storing them in a list would incorrectly count "dog" twice when checking uniqueness, leading to wrong results.
Why It Matters:
When checking isUnique("dog")
with dictionary ["dog", "dog"]
, a naive list-based approach might conclude it's not unique because it finds multiple entries, even though they're all the same word.
The Solution:
Use a set
instead of a list
to store words for each abbreviation. This automatically handles duplicates:
# Correct: Using set
self.abbreviation_map = defaultdict(set)
self.abbreviation_map[abbreviation].add(word) # Duplicates automatically handled
# Incorrect: Using list
self.abbreviation_map = defaultdict(list)
self.abbreviation_map[abbreviation].append(word) # Would store duplicates
2. Incorrect Abbreviation for Short Words
The Pitfall:
Forgetting that words with length less than 3 should not be abbreviated. A common mistake is trying to create abbreviations like "a1"
for "ab" or accessing out-of-bounds indices.
Why It Matters: The problem specifically states that 2-character words remain unchanged. Attempting to abbreviate "it" as "i0t" would be incorrect.
The Solution: Always check the word length before creating an abbreviation:
def _get_abbreviation(self, word: str) -> str:
if len(word) < 3: # Critical check
return word
return f"{word[0]}{len(word) - 2}{word[-1]}"
3. Misunderstanding the Uniqueness Condition
The Pitfall: A common misinterpretation is checking if the word exists in the dictionary rather than checking if its abbreviation conflicts with different words. Some incorrectly implement:
# WRONG: This checks if the word is in dictionary return word not in self.all_words
Why It Matters:
The uniqueness is about abbreviation conflicts, not word existence. If dictionary has ["deer"]
and we check isUnique("door")
, it should return false
because both abbreviate to "d2r", even though "door" isn't in the dictionary.
The Solution: Check if all words with the same abbreviation are identical to the query word:
# Correct: Check if all words with same abbreviation are the same as input
return all(existing_word == word for existing_word in self.abbreviation_map[abbreviation])
4. Not Handling Empty Abbreviation Groups
The Pitfall: When an abbreviation doesn't exist in the map, accessing it without proper handling could cause errors or incorrect logic.
Why It Matters:
If we check isUnique("make")
and no word has abbreviation "m2e", we need to return true
without errors.
The Solution:
Either use defaultdict(set)
or explicitly check for key existence:
# Solution 1: Using defaultdict (preferred)
self.abbreviation_map = defaultdict(set)
# Solution 2: Explicit checking
return (abbreviation not in self.abbreviation_map or
all(word == w for w in self.abbreviation_map[abbreviation]))
Consider the classic dynamic programming of fibonacci numbers, what is the recurrence relation?
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!