Facebook Pixel

1189. Maximum Number of Balloons

EasyHash TableStringCounting
Leetcode Link

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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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:

  1. Count character frequencies: We use Python's Counter from the collections module to count the frequency of each character in text. This gives us a dictionary-like object where keys are characters and values are their counts.

  2. 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 2
    • cnt['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.

  3. 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 Evaluator

Example 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 takes O(n) time
  • The bit shift operations (>>= 1) for 'o' and 'l' are O(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')
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

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

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More