This classic string manipulation problem is a favorite question at companies like Google, Amazon, and Meta because it tests your understanding of character frequency counting and hash map optimization. While it appears simple on the surface, the fundamental concepts about efficiently tracking frequencies lays the groundwork for tackling more complex string-matching and anagram problems.
Problem statement
Given two strings ransomNote and magazine, determine whether ransomNote can be constructed using letters from the magazine. Return true if construction is possible, otherwise return false.
Constraints
- 1 <=
ransomNote.length,magazine.length<= 105 ransomNoteandmagazineconsist of lowercase English letters only.
Examples
ransomNote | magazine | Output | Explanation |
| “d” | “c” | false | The magazine doesn’t contain the letter ‘d’, so we cannot construct the ransom note. |
| “zz” | “yz” | false | The magazine has only one ‘z’, but we need two ‘z’s to construct the ransom note. |
| “abc” | “aabbcc” | true | The magazine contains at least one of each letter (‘a’, ‘b’, ‘c’) needed for the ransomNote. |
| “aab” | “baa” | true | Even though the order is different, the magazine has two ‘a’s and one ‘b’, which is exactly what we need. |
Naive approach: Repeated linear search
A naive approach is to scan through the magazine for each character in the ransomNote. For every character needed, the algorithm searches linearly through the magazine to find a match, then marks it as used to prevent reuse. This continues until either all characters are found or a required character is unavailable.
This approach has a time complexity of O(N × M), where N is the length of the ransomNote and M is the length of the magazine. With both strings containing up to 10⁵ characters, this results in up to 1010 operations, exceeding typical time limits. The core inefficiency stems from treating this as a string matching problem when it’s fundamentally about counting character availability. If the ransomNote contains repeated letters, the entire magazine is scanned separately for each occurrence, creating unnecessary overlapping work.
Optimized approach using HashMap
The optimized approach is essentially the same idea (“find and consume letters from the magazine”), but instead of searching each time, it preprocesses the magazine once into an inventory of available letters. That hash map acts like a fast replacement for “scan and mark used”: each lookup tells you instantly whether a letter is available, and decrementing the count is the efficient version of marking a character as used.
Step-by-step algorithm
- Create an empty hash map
char_count = {}. - Iterate through each character
cinmagazine:- If
cexists inchar_count,- Increment its value by
1. - If
cdoesn’t exist, setchar_count[c] = 1(where the key is the character and the value is its frequency count).
- Increment its value by
- If
- Iterate through each character
cinransomNote:- If
cis not inchar_countorchar_count[c]equals 0,- Return
False.
- Return
- Otherwise,
- Decrement
char_count[c]by 1.
- Decrement
- If
- Return
Trueif all characters are successfully processed.
To better understand the solution, let’s walk through the algorithm using the illustration below:
Python implementation
Time complexity
The time complexity of this algorithm is O(N + M) because:
- We make a single pass through the
magazinestring of length M to build the frequency map, which takes O(M) time. - Then we make a single pass through the
ransomNotestring of length N, performing constant-time hash map lookups and updates for each character, which takes O(N) time.
Space complexity
The space complexity is O(k), where k is the number of distinct characters stored in char_count. Since the strings contain only lowercase English letters, k ≤ 26, so the extra space is effectively constant. Therefore, in this problem’s constraints, the space complexity is O(1).
Common pitfalls and interview tips
- The Empty String Edge Case: An empty ransom note (
"") should always returntruebecause we need zero letters to construct nothing. Some candidates add special-case handling, but the algorithm naturally handles it; the loop simply doesn’t execute. - Magazine vs. Ransom Note Order: A common mistake is building the frequency map from the ransom note instead of the magazine. Remember that the magazine is our resource pool, and the ransom note is our shopping list. We count what we have (magazine), then check what we need (ransom note).
- Off-by-One with Decrementing: After checking if a character exists and has count > 0, make sure to decrement before checking the next character. Some candidates forget to decrement, leading to incorrect results when the same letter appears multiple times in the ransom note.