1189. Maximum Number of Balloons
Problem Description
You are given a string text
and need to determine how many times you can form the word "balloon" using the characters from text
.
The word "balloon" contains:
- 1 'b'
- 1 'a'
- 2 'l's
- 2 'o's
- 1 'n'
Each character in text
can only be used once. Your task is to find the maximum number of complete instances of "balloon" that can be formed.
For example, if text = "nlaebolko"
, you can form exactly 1 "balloon" because even though you have enough of most letters, you only have 1 'b', 1 'a', and 1 'n'.
The solution counts the frequency of each character in text
, then adjusts for the fact that "balloon" needs 2 'l's and 2 'o's by dividing their counts by 2. Finally, it finds the minimum count among all required letters ('b', 'a', 'l', 'o', 'n'), which determines the maximum number of "balloon" instances possible.
Intuition
Think of this problem like a recipe. To make one "balloon", we need specific ingredients: 1 'b', 1 'a', 2 'l's, 2 'o's, and 1 'n'. The question becomes: given our available letters in text
, how many complete sets of these ingredients can we make?
The key insight is that we're limited by whichever letter we have the least of (relative to what we need). It's like making sandwiches - if you have 100 slices of bread but only 2 slices of cheese, you can only make 2 cheese sandwiches.
First, we count how many of each letter we have in text
. But here's the clever part: since "balloon" needs 2 'l's and 2 'o's, having 4 'l's is really like having enough for 2 balloons, not 4. So we divide the counts of 'l' and 'o' by 2 to normalize them to the same scale as the other letters.
After this adjustment, each letter's count represents how many "balloon"s that particular letter could support. The minimum among these counts is our answer because we can't make more "balloon"s than what our scarcest letter allows.
For instance, if after adjustment we have: 'b':3, 'a':5, 'l':2, 'o':4, 'n':1, then we can only make 1 "balloon" because 'n' is our bottleneck.
Solution Approach
The implementation uses a frequency counting approach with a clever normalization step:
-
Count character frequencies: We use Python's
Counter
from the collections module to count the frequency of each character intext
. This gives us a dictionary-like object where keys are characters and values are their counts. -
Normalize for double letters: Since "balloon" contains 2 'l's and 2 'o's, we need to adjust their counts. The code uses the right shift operator
>>=
to divide by 2:cnt['o'] >>= 1
divides the count of 'o' by 2cnt['l'] >>= 1
divides the count of 'l' by 2
This normalization ensures that if we have 4 'o's, we can only make 2 "balloon"s from them, not 4.
-
Find the minimum: We iterate through each character in 'balon' (note it's 'balon' not 'balloon' to avoid counting 'l' and 'o' twice) and find the minimum count using
min(cnt[c] for c in 'balon')
. This minimum represents the bottleneck - the maximum number of complete "balloon"s we can form.
The algorithm is efficient with O(n)
time complexity where n
is the length of the input string (for counting characters), and O(1)
space complexity since we're only storing counts for at most 26 lowercase letters.
The beauty of this solution lies in its simplicity - by normalizing the counts of repeated letters and finding the minimum, we directly get the answer without complex logic or multiple passes through the data.
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 the solution with text = "loonbalxballpoon"
.
Step 1: Count character frequencies First, we count how many of each character we have:
- 'l': 4 occurrences
- 'o': 4 occurrences
- 'n': 2 occurrences
- 'b': 2 occurrences
- 'a': 2 occurrences
- 'x': 1 occurrence (not needed for "balloon")
- 'p': 1 occurrence (not needed for "balloon")
Step 2: Normalize for double letters Since "balloon" needs 2 'l's and 2 'o's per instance, we divide their counts by 2:
- 'l': 4 ÷ 2 = 2 (meaning we have enough 'l's for 2 balloons)
- 'o': 4 ÷ 2 = 2 (meaning we have enough 'o's for 2 balloons)
- 'n': 2 (enough for 2 balloons)
- 'b': 2 (enough for 2 balloons)
- 'a': 2 (enough for 2 balloons)
Step 3: Find the minimum After normalization, all required letters have a count of 2:
- 'b': 2
- 'a': 2
- 'l': 2 (normalized)
- 'o': 2 (normalized)
- 'n': 2
The minimum is 2, so we can form exactly 2 instances of "balloon".
To verify: From "loonbalxballpoon", we can extract:
- First "balloon": b-a-l-l-o-o-n (using letters at various positions)
- Second "balloon": b-a-l-l-o-o-n (using the remaining letters)
All letters are accounted for, confirming our answer of 2.
Solution Implementation
1from collections import Counter
2
3class Solution:
4 def maxNumberOfBalloons(self, text: str) -> int:
5 # Count frequency of each character in the input text
6 char_count = Counter(text)
7
8 # 'balloon' has 1 'b', 1 'a', 2 'l's, 2 'o's, 1 'n'
9 # Divide count of 'l' and 'o' by 2 since we need 2 of each per 'balloon'
10 char_count['o'] //= 2
11 char_count['l'] //= 2
12
13 # Find the minimum count among required characters
14 # This determines the maximum number of 'balloon' words we can form
15 return min(char_count[c] for c in 'balon')
16
1class Solution {
2 public int maxNumberOfBalloons(String text) {
3 // Create frequency array for all 26 lowercase letters
4 int[] charFrequency = new int[26];
5
6 // Count frequency of each character in the input text
7 for (int i = 0; i < text.length(); i++) {
8 charFrequency[text.charAt(i) - 'a']++;
9 }
10
11 // The word "balloon" contains 2 'l's and 2 'o's
12 // Divide their counts by 2 to get the actual number of times they can be used
13 charFrequency['l' - 'a'] /= 2;
14 charFrequency['o' - 'a'] /= 2;
15
16 // Initialize result with a large value
17 int maxBalloons = Integer.MAX_VALUE;
18
19 // Check the minimum frequency among all required characters
20 // "balon" contains unique characters from "balloon" (b, a, l, o, n)
21 for (char c : "balon".toCharArray()) {
22 maxBalloons = Math.min(maxBalloons, charFrequency[c - 'a']);
23 }
24
25 return maxBalloons;
26 }
27}
28
1class Solution {
2public:
3 int maxNumberOfBalloons(string text) {
4 // Array to count frequency of each letter (a-z)
5 int letterCount[26] = {0};
6
7 // Count the frequency of each character in the input text
8 for (char c : text) {
9 letterCount[c - 'a']++;
10 }
11
12 // "balloon" has 2 'l's and 2 'o's, so divide their counts by 2
13 // to get the number of complete sets
14 letterCount['o' - 'a'] /= 2;
15 letterCount['l' - 'a'] /= 2;
16
17 // Initialize result to a large number
18 int maxBalloons = INT_MAX;
19
20 // Check each unique letter in "balloon" (b, a, l, o, n)
21 string balloonLetters = "balon";
22 for (char c : balloonLetters) {
23 // The maximum number of "balloon" words is limited by
24 // the letter with minimum available count
25 maxBalloons = min(maxBalloons, letterCount[c - 'a']);
26 }
27
28 return maxBalloons;
29 }
30};
31
1function maxNumberOfBalloons(text: string): number {
2 // Create an array to count occurrences of each letter (a-z)
3 const letterCount = new Array(26).fill(0);
4
5 // Count the frequency of each character in the input text
6 for (const character of text) {
7 // Convert character to index (0-25) by subtracting ASCII value of 'a'
8 const index = character.charCodeAt(0) - 97;
9 letterCount[index]++;
10 }
11
12 // Calculate maximum number of "balloon" words that can be formed
13 // The word "balloon" contains: b(1), a(1), l(2), o(2), n(1)
14 // Index mapping: a=0, b=1, l=11, n=13, o=14
15 const countA = letterCount[0]; // 'a' appears once in "balloon"
16 const countB = letterCount[1]; // 'b' appears once in "balloon"
17 const countL = letterCount[11] >> 1; // 'l' appears twice, so divide by 2 using right shift
18 const countO = letterCount[14] >> 1; // 'o' appears twice, so divide by 2 using right shift
19 const countN = letterCount[13]; // 'n' appears once in "balloon"
20
21 // Return the minimum count as it determines the bottleneck
22 return Math.min(countA, countB, countL, countO, countN);
23}
24
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the string text
. This is because:
- Creating the Counter object requires iterating through all characters in
text
once, which takesO(n)
time - The bit shift operations (
>>= 1
) for 'o' and 'l' areO(1)
operations - Finding the minimum among 5 specific characters ('b', 'a', 'l', 'o', 'n') takes
O(5) = O(1)
time
The space complexity is O(C)
, where C
is the size of the character set. In this problem, C = 26
(for lowercase English letters). The Counter object stores at most C
distinct characters from the input string, resulting in O(C)
space usage. Since C
is a constant (26), this can also be expressed as O(1)
space complexity.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Missing Characters Causing KeyError
The most common pitfall in this solution is that if any required character ('b', 'a', 'l', 'o', 'n') is missing from the input text, accessing char_count[c]
will raise a KeyError since Counter
doesn't automatically return 0 for missing keys when using bracket notation.
Example of the issue:
text = "xyz" # No characters from 'balloon'
char_count = Counter(text)
# This will crash with KeyError: 'b'
return min(char_count[c] for c in 'balon')
Solution:
Use the .get()
method with a default value of 0, or check if the character exists first:
def maxNumberOfBalloons(self, text: str) -> int:
char_count = Counter(text)
char_count['o'] //= 2
char_count['l'] //= 2
# Safe approach using .get() with default value
return min(char_count.get(c, 0) for c in 'balon')
2. Integer Division vs Right Shift Confusion
While the problem description mentions using the right shift operator >>=
, the actual code uses floor division //=
. Both achieve the same result for positive integers (dividing by 2), but mixing them up or using regular division /=
would cause issues.
Incorrect approach:
char_count['o'] /= 2 # Results in float, not integer char_count['l'] /= 2 # Will cause issues in min() comparison
Correct approaches (both work):
# Floor division char_count['o'] //= 2 char_count['l'] //= 2 # OR right shift (equivalent for dividing by 2) char_count['o'] >>= 1 char_count['l'] >>= 1
3. Forgetting to Handle Empty String
While Counter
handles empty strings gracefully, the logic should still account for edge cases where the input is empty or contains no valid characters.
Robust solution combining all fixes:
def maxNumberOfBalloons(self, text: str) -> int:
if not text:
return 0
char_count = Counter(text)
# Check if all required characters exist
required = {'b', 'a', 'l', 'o', 'n'}
if not required.issubset(char_count.keys()):
return 0
char_count['o'] //= 2
char_count['l'] //= 2
return min(char_count[c] for c in 'balon')
You are given an array of intervals where intervals[i] = [start_i, end_i]
represent the start and end of the ith
interval. You need to merge all overlapping intervals and return an array of the non-overlapping intervals that cover all the intervals in the input.
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!