title: Check if All Characters Have Equal Number of Occurrences
slug: check-if-all-characters-have-equal-number-of-occurrences
difficulty: easy
leetcode_id: 1941
leetcode_url: https://leetcode.com/problems/check-if-all-characters-have-equal-number-of-occurrences/
categories:
  - strings
  - hash-tables
patterns:
  - prefix-sum

description: |
  Given a string `s`, return `true` *if* `s` *is a **good** string, or* `false` *otherwise*.

  A string `s` is **good** if **all** the characters that appear in `s` have the **same number of occurrences** (i.e., the same frequency).

constraints: |
  - `1 <= s.length <= 1000`
  - `s` consists of lowercase English letters.

examples:
  - input: 's = "abacbc"'
    output: "true"
    explanation: "The characters that appear in s are 'a', 'b', and 'c'. All characters occur 2 times in s."
  - input: 's = "aaabb"'
    output: "false"
    explanation: "The characters that appear in s are 'a' and 'b'. 'a' occurs 3 times while 'b' occurs 2 times, which is not the same number of times."

explanation:
  intuition: |
    Think of this problem as counting items in different buckets, then checking if all buckets have the same number of items.

    Imagine you have a collection of coloured marbles and you want to know if every colour appears the same number of times. You would first count how many marbles of each colour you have, then compare all these counts.

    The key insight is that we need to do two things:
    1. **Count** how many times each character appears
    2. **Compare** all those counts to see if they're all equal

    If every frequency is the same, the string is "good". Otherwise, it's not. This naturally leads to using a hash table to store counts, then checking if all values in the hash table are identical.

  approach: |
    We solve this using a **Hash Map with Set Comparison** approach:

    **Step 1: Count character frequencies**

    - Create a hash map (dictionary) to store the frequency of each character
    - Iterate through the string and increment the count for each character
    - After this step, we have a mapping from each unique character to its count

    &nbsp;

    **Step 2: Check if all frequencies are equal**

    - Extract all the frequency values from our hash map
    - Convert these values to a set (which removes duplicates)
    - If the set has exactly one element, all frequencies were equal
    - If the set has more than one element, frequencies differ

    &nbsp;

    **Step 3: Return the result**

    - Return `true` if there's only one unique frequency
    - Return `false` otherwise

    &nbsp;

    This approach works because a set automatically eliminates duplicates. If all characters have the same frequency (say, 2), then converting `[2, 2, 2]` to a set gives `{2}` with length 1.

  common_pitfalls:
    - title: Counting Characters Not in the String
      description: |
        A mistake is to check if *all 26 letters* have the same frequency. This would always return `false` for most strings because letters not present have frequency 0.

        The problem asks about characters that **appear** in the string. Only count and compare characters that actually exist in the input.
      wrong_approach: "Checking all 26 letters have equal frequency"
      correct_approach: "Only compare frequencies of characters present in the string"

    - title: Forgetting Single Character Strings
      description: |
        For a string like `"a"`, there's only one character with frequency 1. This should return `true` since the single character trivially has equal frequency with itself.

        Edge case: A string with only one unique character is always "good", regardless of how many times it repeats.
      wrong_approach: "Not handling edge case where only one character type exists"
      correct_approach: "The set-based solution handles this naturally since a single frequency means set size is 1"

    - title: Using Sorting Instead of Sets
      description: |
        Some might sort the frequencies and then compare adjacent elements. While this works, it's less efficient — sorting takes O(k log k) where k is the number of unique characters.

        Using a set is simpler and can be O(k) for insertion and comparison, making the code both cleaner and faster.
      wrong_approach: "Sorting frequencies and comparing"
      correct_approach: "Using a set to check for unique frequency count"

  key_takeaways:
    - "**Hash map for counting**: Frequency counting is a fundamental hash table pattern that appears in many string problems"
    - "**Set for uniqueness check**: Converting to a set and checking its size is an elegant way to verify if all values are equal"
    - "**Problem decomposition**: Break down into two clear steps — count first, then compare"
    - "**Related problems**: This technique extends to problems like finding if an array can be rearranged, checking for anagrams, and other frequency-based challenges"

  time_complexity: "O(n). We iterate through the string once to count frequencies, then iterate through the frequency map once to build the set."
  space_complexity: "O(k) where k is the number of unique characters. In the worst case with lowercase English letters, k ≤ 26, so this is effectively O(1)."

solutions:
  - approach_name: Hash Map with Set
    is_optimal: true
    code: |
      from collections import Counter

      def are_occurrences_equal(s: str) -> bool:
          # Count frequency of each character
          freq = Counter(s)

          # Get all frequency values and convert to set
          # If all frequencies are equal, set will have size 1
          unique_frequencies = set(freq.values())

          return len(unique_frequencies) == 1
    explanation: |
      **Time Complexity:** O(n) — We traverse the string once to build the counter.

      **Space Complexity:** O(k) — We store at most k unique characters, where k ≤ 26 for lowercase letters.

      Using Python's `Counter` makes the counting step concise. The set conversion elegantly handles the comparison — if the set has exactly one element, all frequencies were identical.

  - approach_name: Manual Counting with First Comparison
    is_optimal: false
    code: |
      def are_occurrences_equal(s: str) -> bool:
          # Build frequency map manually
          freq = {}
          for char in s:
              freq[char] = freq.get(char, 0) + 1

          # Get frequencies as a list
          counts = list(freq.values())

          # Compare all frequencies to the first one
          first_count = counts[0]
          for count in counts:
              if count != first_count:
                  return False

          return True
    explanation: |
      **Time Complexity:** O(n) — Single pass to count, then iterate through unique characters.

      **Space Complexity:** O(k) — Same as optimal, storing up to k character frequencies.

      This approach is more explicit but less Pythonic. We manually build the frequency map, then compare each frequency to the first. It achieves the same result but with more code.
