title: Check if Number Has Equal Digit Count and Digit Value
slug: check-if-number-has-equal-digit-count-and-digit-value
difficulty: easy
leetcode_id: 2283
leetcode_url: https://leetcode.com/problems/check-if-number-has-equal-digit-count-and-digit-value/
categories:
  - strings
  - hash-tables
patterns:
  - prefix-sum

function_signature: "def digit_count(num: str) -> bool:"

test_cases:
  visible:
    - input: { num: "1210" }
      expected: true
    - input: { num: "030" }
      expected: false
  hidden:
    - input: { num: "2020" }
      expected: true
    - input: { num: "0" }
      expected: false
    - input: { num: "10" }
      expected: false
    - input: { num: "21200" }
      expected: true
    - input: { num: "1111" }
      expected: false
    - input: { num: "2100" }
      expected: true

description: |
  You are given a **0-indexed** string `num` of length `n` consisting of digits.

  Return `true` *if for **every** index* `i` *in the range* `0 <= i < n`*, the digit* `i` *occurs* `num[i]` *times in* `num`*, otherwise return* `false`.

constraints: |
  - `n == num.length`
  - `1 <= n <= 10`
  - `num` consists only of digits

examples:
  - input: 'num = "1210"'
    output: "true"
    explanation: |
      num[0] = '1'. The digit 0 occurs once in num.
      num[1] = '2'. The digit 1 occurs twice in num.
      num[2] = '1'. The digit 2 occurs once in num.
      num[3] = '0'. The digit 3 occurs zero times in num.
      The condition holds true for every index in "1210", so return true.
  - input: 'num = "030"'
    output: "false"
    explanation: |
      num[0] = '0'. The digit 0 should occur zero times, but actually occurs twice in num.
      num[1] = '3'. The digit 1 should occur three times, but actually occurs zero times in num.
      num[2] = '0'. The digit 2 occurs zero times in num.
      The indices 0 and 1 both violate the condition, so return false.

explanation:
  intuition: |
    Think of this problem as a **self-describing number** puzzle. Each position in the string tells you how many times that position's index should appear as a digit elsewhere in the string.

    Imagine the string as a set of rules: position 0 says "the digit 0 must appear exactly `num[0]` times", position 1 says "the digit 1 must appear exactly `num[1]` times", and so on. Your job is to verify that the string follows its own rules.

    The key insight is that we need to **count the occurrences** of each digit first, then check if each position's value matches the count for its corresponding index. This is a classic counting problem where we build a frequency map and then validate against it.

  approach: |
    We solve this using a **Counting Approach**:

    **Step 1: Count digit frequencies**

    - Create a frequency array or hash map to count how many times each digit (0-9) appears in the string
    - Iterate through the string once to populate these counts

    &nbsp;

    **Step 2: Validate the self-describing property**

    - For each index `i` from `0` to `n-1`:
      - Get the expected count from `num[i]` (convert character to integer)
      - Get the actual count of digit `i` from our frequency map
      - If they don't match, return `false`

    &nbsp;

    **Step 3: Return the result**

    - If all positions pass validation, return `true`

    &nbsp;

    This approach works because we separate the problem into two clear phases: counting and validating. The counting phase gives us the ground truth, and the validation phase checks if the string's claims match reality.

  common_pitfalls:
    - title: Confusing Index and Value
      description: |
        The problem has a subtle twist: at index `i`, the *value* `num[i]` tells us how many times the *digit* `i` should appear.

        For example, at index 2, if `num[2] = '1'`, this means the digit `2` should appear exactly once in the string — not that the digit `1` should appear twice.

        Read carefully: we're checking if digit `i` occurs `num[i]` times.
      wrong_approach: "Checking if digit num[i] occurs i times"
      correct_approach: "Checking if digit i occurs num[i] times"

    - title: Character vs Integer Conversion
      description: |
        Remember that `num` is a string, so `num[i]` gives a character like `'2'`, not the integer `2`.

        You need to convert: `int(num[i])` in Python, or `num[i] - '0'` in languages like C++ or Java.

        Forgetting this conversion leads to comparing characters with integers, which will give wrong results or type errors.
      wrong_approach: "Comparing num[i] directly with count"
      correct_approach: "Convert num[i] to integer before comparison"

    - title: Not Handling All Indices
      description: |
        Since the string has length `n`, we only need to check indices `0` through `n-1`. We only care about digits `0` through `n-1` as well, since larger digits can't be valid indices.

        However, digits larger than `n-1` appearing in the string is fine — they just won't be checked as indices. The constraint `n <= 10` ensures all digits 0-9 are potentially valid.

  key_takeaways:
    - "**Self-describing structures**: Some problems define constraints that reference themselves — break these into counting and validation phases"
    - "**Frequency counting pattern**: Building a frequency map first simplifies many validation problems"
    - "**Index vs value awareness**: Pay close attention to what the index represents vs what the value represents"
    - "**Small constraint optimisation**: With `n <= 10`, even O(n^2) would work, but O(n) is cleaner and teaches better habits"

  time_complexity: "O(n). We iterate through the string twice: once to count frequencies, once to validate."
  space_complexity: "O(1). We use a fixed-size frequency array of 10 elements (for digits 0-9), regardless of input size."

solutions:
  - approach_name: Frequency Counting
    is_optimal: true
    code: |
      def digit_count(num: str) -> bool:
          # Count occurrences of each digit (0-9)
          freq = [0] * 10
          for char in num:
              freq[int(char)] += 1

          # Check if each index i has digit i appearing num[i] times
          for i in range(len(num)):
              expected = int(num[i])  # How many times digit i should appear
              actual = freq[i]        # How many times digit i actually appears
              if expected != actual:
                  return False

          return True
    explanation: |
      **Time Complexity:** O(n) — Two passes through the string of length n.

      **Space Complexity:** O(1) — Fixed array of 10 integers.

      We first count all digit occurrences, then verify each position's claim. The fixed-size frequency array makes this space-efficient.

  - approach_name: Counter with Pythonic Check
    is_optimal: true
    code: |
      from collections import Counter

      def digit_count(num: str) -> bool:
          # Count all digit occurrences
          count = Counter(num)

          # Check each index: digit i should occur num[i] times
          for i, char in enumerate(num):
              expected = int(char)
              actual = count.get(str(i), 0)  # Get count of digit i (as string)
              if expected != actual:
                  return False

          return True
    explanation: |
      **Time Complexity:** O(n) — Counter creation and validation loop.

      **Space Complexity:** O(k) — Where k is the number of unique digits (at most 10).

      This approach uses Python's Counter for cleaner counting. Note that we look up `str(i)` since Counter keys are characters, not integers.

  - approach_name: One-Liner (Pythonic)
    is_optimal: true
    code: |
      from collections import Counter

      def digit_count(num: str) -> bool:
          count = Counter(num)
          return all(count.get(str(i), 0) == int(c) for i, c in enumerate(num))
    explanation: |
      **Time Complexity:** O(n) — Single pass with generator expression.

      **Space Complexity:** O(k) — Counter storage for unique digits.

      A concise one-liner using `all()` with a generator. For each position, we check if the count of that index-as-digit matches the expected value. Elegant but less readable for beginners.
