Facebook Pixel

1941. Check if All Characters Have Equal Number of Occurrences

EasyHash TableStringCounting
Leetcode Link

Problem Description

Given a string s, you need to determine if it's a "good" string. A string is considered good if every character that appears in the string has exactly the same number of occurrences (frequency).

For example:

  • "abacbc" is a good string because 'a', 'b', and 'c' each appear exactly 2 times
  • "aaabb" is not a good string because 'a' appears 3 times while 'b' appears 2 times

The solution uses Python's Counter to count character frequencies, then checks if all frequency values are the same by converting them to a set. If the set has only 1 unique value, it means all characters have the same frequency, making it a good string.

The approach:

  1. Count the frequency of each character using Counter(s)
  2. Extract all frequency values with .values()
  3. Convert to a set to get unique frequencies
  4. If the set has exactly 1 element, all frequencies are equal, so return True
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is that we don't need to know what the actual frequency value is - we just need to verify that all characters share the same frequency.

Think about it this way: if we count how many times each character appears and then look at all these counts, a "good" string would have all identical counts. For instance, if we have counts like [2, 2, 2], that's good. But [2, 3, 2] would be bad because not all counts match.

The clever part is realizing that we can use a set to check uniqueness. Sets automatically remove duplicates, so if we put all our frequency counts into a set and it ends up with just one element, that means all the original counts were the same number.

For example:

  • String "aabbcc" → frequencies: {'a': 2, 'b': 2, 'c': 2} → frequency values: [2, 2, 2] → set of values: {2} → size = 1 ✓
  • String "aaabbc" → frequencies: {'a': 3, 'b': 2, 'c': 1} → frequency values: [3, 2, 1] → set of values: {3, 2, 1} → size = 3 ✗

This leads us naturally to the one-line solution: count frequencies, extract the values, convert to set, and check if the set size equals 1.

Solution Approach

The implementation uses a hash table (or counter) to track character frequencies, following these steps:

  1. Count Character Frequencies: We use Python's Counter class from the collections module to create a hash table that maps each character to its frequency count. Counter(s) automatically counts occurrences of each character in the string s.

  2. Extract Frequency Values: Once we have the counter object, we call .values() to get just the frequency counts, ignoring which characters they belong to. For example, if the counter is {'a': 2, 'b': 2, 'c': 2}, then .values() gives us [2, 2, 2].

  3. Check Uniqueness Using Set: We convert the frequency values to a set using set(). Since sets only store unique elements, duplicate frequencies are automatically removed. If all characters have the same frequency, the set will contain exactly one element.

  4. Return Result: We check if len(set(Counter(s).values())) == 1. If true, all characters have equal occurrences and the string is "good".

Alternative Implementation: As mentioned in the reference, we could also use an array cnt of size 26 (for lowercase English letters) where cnt[i] stores the count of the i-th letter. After counting, we'd iterate through the array and verify that all non-zero counts are equal.

Time Complexity: O(n) where n is the length of the string, as we need to traverse the string once to count frequencies.

Space Complexity: O(k) where k is the number of unique characters in the string, which is at most 26 for lowercase English letters.

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 the string s = "abacbc".

Step 1: Count Character Frequencies We use Counter(s) to count each character:

  • 'a' appears at positions 0, 2 → count = 2
  • 'b' appears at positions 1, 4 → count = 2
  • 'c' appears at positions 3, 5 → count = 2

This gives us: Counter({'a': 2, 'b': 2, 'c': 2})

Step 2: Extract Frequency Values We call .values() on our counter to get just the counts: [2, 2, 2]

Step 3: Convert to Set for Uniqueness Check Converting [2, 2, 2] to a set removes duplicates: set([2, 2, 2]){2}

Step 4: Check Set Size The set has exactly 1 element, so len({2}) == 1 returns True.

Therefore, "abacbc" is a good string! ✓

Counter-example: Let's quickly check s = "aaabb":

  1. Counter("aaabb"){'a': 3, 'b': 2}
  2. .values()[3, 2]
  3. set([3, 2]){3, 2}
  4. len({3, 2}) == 1False

Since the set has 2 different values, "aaabb" is not a good string. ✗

Solution Implementation

1from collections import Counter
2
3class Solution:
4    def areOccurrencesEqual(self, s: str) -> bool:
5        # Count the frequency of each character in the string
6        char_frequency = Counter(s)
7      
8        # Get all frequency values from the counter
9        frequency_values = char_frequency.values()
10      
11        # Convert frequency values to a set to get unique counts
12        # If all characters have the same frequency, the set will have exactly 1 element
13        unique_frequencies = set(frequency_values)
14      
15        # Return True if there's only one unique frequency value
16        return len(unique_frequencies) == 1
17
1class Solution {
2    /**
3     * Checks if all characters in the string have equal frequency of occurrences.
4     * 
5     * @param s the input string to check
6     * @return true if all characters appear the same number of times, false otherwise
7     */
8    public boolean areOccurrencesEqual(String s) {
9        // Array to store frequency count for each letter (a-z)
10        int[] frequencyCount = new int[26];
11      
12        // Count the frequency of each character in the string
13        for (char character : s.toCharArray()) {
14            frequencyCount[character - 'a']++;
15        }
16      
17        // Variable to store the expected frequency (first non-zero frequency found)
18        int expectedFrequency = 0;
19      
20        // Check if all non-zero frequencies are equal
21        for (int currentFrequency : frequencyCount) {
22            // Skip characters that don't appear in the string
23            if (currentFrequency == 0) {
24                continue;
25            }
26          
27            // If we've found a frequency before and it doesn't match current, return false
28            if (expectedFrequency > 0 && expectedFrequency != currentFrequency) {
29                return false;
30            }
31          
32            // Set the expected frequency to the first non-zero frequency found
33            expectedFrequency = currentFrequency;
34        }
35      
36        // All characters have equal occurrences
37        return true;
38    }
39}
40
1class Solution {
2public:
3    bool areOccurrencesEqual(string s) {
4        // Create a frequency array to count occurrences of each letter (a-z)
5        vector<int> frequency(26, 0);
6      
7        // Count the frequency of each character in the string
8        for (char ch : s) {
9            frequency[ch - 'a']++;
10        }
11      
12        // Variable to store the first non-zero frequency value as reference
13        int referenceFrequency = 0;
14      
15        // Check if all non-zero frequencies are equal
16        for (int count : frequency) {
17            // Skip characters that don't appear in the string
18            if (count == 0) {
19                continue;
20            }
21          
22            // If we haven't set a reference frequency yet, set it
23            if (referenceFrequency == 0) {
24                referenceFrequency = count;
25            }
26            // If current frequency doesn't match the reference, return false
27            else if (referenceFrequency != count) {
28                return false;
29            }
30        }
31      
32        // All non-zero frequencies are equal
33        return true;
34    }
35};
36
1/**
2 * Checks if all characters in the string have the same frequency of occurrence
3 * @param s - Input string to check
4 * @returns true if all characters appear the same number of times, false otherwise
5 */
6function areOccurrencesEqual(s: string): boolean {
7    // Initialize array to count frequency of each letter (26 letters in alphabet)
8    const characterCounts: number[] = Array(26).fill(0);
9  
10    // Count occurrences of each character in the string
11    for (const character of s) {
12        // Convert character to index (0-25) by subtracting 'a' ASCII value
13        const index: number = character.charCodeAt(0) - 'a'.charCodeAt(0);
14        characterCounts[index]++;
15    }
16  
17    // Find the first non-zero frequency value to use as reference
18    const referenceFrequency: number | undefined = characterCounts.find((frequency: number) => frequency > 0);
19  
20    // Check if all non-zero frequencies are equal to the reference frequency
21    // Returns true if every character either doesn't appear (frequency === 0) 
22    // or appears the same number of times as the reference
23    return characterCounts.every((frequency: number) => frequency === 0 || frequency === referenceFrequency);
24}
25

Time and Space Complexity

The time complexity is O(n), where n is the length of the string s. This is because:

  • Counter(s) iterates through all characters in the string once to count their occurrences, which takes O(n) time
  • .values() retrieves the frequency values in O(1) time
  • set() conversion iterates through the frequency values, which in the worst case is O(|Σ|) where |Σ| = 26 for lowercase English letters
  • len() operation is O(1)
  • Since |Σ| is a constant (26), the overall time complexity remains O(n)

The space complexity is O(|Σ|), where |Σ| is the size of the character set. In this problem:

  • Counter(s) creates a dictionary that stores at most |Σ| unique characters and their counts
  • set(Counter(s).values()) creates a set of frequency values, which is also bounded by O(|Σ|) in the worst case
  • For lowercase English letters, |Σ| = 26, making the space complexity O(26) = O(1) constant space

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Empty String Handling

One common pitfall is not considering what happens when the input string is empty. An empty string has no characters, so Counter("") returns an empty counter, and Counter("").values() returns an empty collection. When converted to a set, this becomes an empty set with length 0, causing the function to return False. However, philosophically, an empty string could be considered "good" since there are no characters with differing frequencies.

Solution: Add explicit handling for empty strings if the problem requires it:

def areOccurrencesEqual(self, s: str) -> bool:
    if not s:  # Handle empty string case
        return True  # or False, depending on problem requirements
    return len(set(Counter(s).values())) == 1

2. Case Sensitivity

The current solution treats uppercase and lowercase letters as different characters. For example, "aAbB" would return False because 'a' appears once, 'A' appears once, 'b' appears once, and 'B' appears once - all have the same frequency, but if the problem intended to treat 'a' and 'A' as the same character, this would be incorrect.

Solution: Normalize the string to handle case-insensitive comparisons:

def areOccurrencesEqual(self, s: str) -> bool:
    s = s.lower()  # Convert to lowercase for case-insensitive comparison
    return len(set(Counter(s).values())) == 1

3. Special Characters and Spaces

The solution counts all characters including spaces, special characters, and digits. If the string is "a a", it would count 'a' twice and space once, returning False. This might not be the intended behavior if only alphabetic characters should be considered.

Solution: Filter out non-alphabetic characters if needed:

def areOccurrencesEqual(self, s: str) -> bool:
    # Filter to keep only alphabetic characters
    s = ''.join(c for c in s if c.isalpha())
    if not s:
        return True
    return len(set(Counter(s).values())) == 1

4. Single Character String Edge Case

While not technically a bug, it's worth noting that a string with only one unique character (like "aaaa") will always return True since there's only one frequency value. Some might overlook testing this edge case.

Solution: The current implementation handles this correctly, but ensure test cases cover:

  • Single character repeated multiple times: "aaaa"True
  • Single character appearing once: "a"True
  • Multiple different single characters: "abc"True
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of the following uses divide and conquer strategy?


Recommended Readings

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

Load More