Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

1067. Digit Count in Range - Leetcode Solution

Problem Description

You are given four integers: num1, num2, digit, and you need to count how many times the digit digit appears in all numbers in the inclusive range from num1 to num2.

  • num1 and num2 define the range (inclusive), where num1 <= num2.
  • digit is a single-digit integer (0-9).
  • Return the total number of times digit appears in all numbers from num1 to num2 (inclusive), across all digits of every number.

Constraints: The function must work efficiently even if num2 is large (e.g., up to 109).

Thought Process

The most direct approach is to iterate through every number from num1 to num2, convert each number to a string, and count how many times digit appears. This is simple and intuitive, but it will be too slow for large ranges because it requires examining each number individually.

To optimize, we need a way to efficiently count the digit occurrences in a range without examining every single number. This leads us to digit dynamic programming (digit DP) or mathematical counting techniques, where we count how many times digit appears from 0 to N and use this to calculate the answer for any arbitrary range.

Solution Approach

We want to efficiently count how many times digit appears from num1 to num2. The key idea is:

  • Let f(x, d) be the number of times digit d appears in all numbers from 0 to x (inclusive).
  • Then, the answer for range [num1, num2] is f(num2, digit) - f(num1 - 1, digit).
  • To compute f(x, d) efficiently, we use digit position analysis:
    • For each position (units, tens, hundreds, etc.), we calculate how many times d appears in that position across all numbers from 0 to x.
    • We repeat this for each digit position, summing up contributions.

The main challenge is handling the leading zeros and the special case when digit == 0 (since numbers do not have leading zeros).

  1. Define a helper function count_digit(n, d) to count occurrences of d from 0 to n.
  2. For each digit position, calculate:
    • higher = n // (10^{pos+1}) (digits to the left)
    • current = (n // 10^{pos}) % 10 (digit at current position)
    • lower = n % (10^{pos}) (digits to the right)
  3. For each position:
    • If current < d: The digit d appears higher * 10^{pos} times at this position.
    • If current == d: It appears higher * 10^{pos} + (lower + 1) times.
    • If current > d: It appears (higher + 1) * 10^{pos} times.
    • Special case for d == 0: We must not count leading zeros, so we skip the highest position if pos is the most significant digit.
  4. Sum up the contributions for all positions.
  5. Return count_digit(num2, digit) - count_digit(num1-1, digit) as the answer.

Example Walkthrough

Example: Suppose num1 = 25, num2 = 35, digit = 3.

  1. Numbers in the range: 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35
  2. Count how many times 3 appears:
    • 30: once (tens place)
    • 31: once
    • 32: once
    • 33: twice (both tens and ones)
    • 34: once
    • 35: once
  3. From 25-29: 0 times
  4. From 30-39: 30 (1), 31 (1), 32 (1), 33 (2), 34 (1), 35 (1) = 7 times
  5. So the answer is 7.

The optimized algorithm computes this for any range, even for very large numbers, by analyzing digit positions.

Time and Space Complexity

Brute-force approach:

  • Time: O(N * D), where N is the range size (num2 - num1 + 1), and D is the number of digits per number (up to 10 for large numbers).
  • Space: O(1), as we only need counters.
  • This approach is too slow for large ranges.
Optimized approach (digit position analysis):
  • Time: O(D), where D is the number of digits in num2 (at most 10 for 32-bit integers).
  • Space: O(1), as we only use a few variables.
  • This is efficient and suitable for large inputs.

Summary

The main insight is to avoid brute-force counting by using digit position analysis. By counting how many times the digit appears in each position across all numbers up to n, we can efficiently solve the problem for any range and any digit, even for very large numbers. This approach leverages mathematical properties of numbers and is a classic example of digit dynamic programming.

Code Implementation

def countDigitOccurrences(num1: int, num2: int, digit: int) -> int:
    def count_digit(n: int, d: int) -> int:
        if n < 0:
            return 0
        res = 0
        str_n = str(n)
        length = len(str_n)
        for pos in range(length):
            power = 10 ** (length - pos - 1)
            current_digit = int(str_n[pos])
            left = int(str_n[:pos]) if pos > 0 else 0
            right = int(str_n[pos+1:]) if pos < length - 1 else 0

            if d == 0 and pos == 0:
                continue  # skip leading zero count

            if current_digit < d:
                res += left * power
            elif current_digit == d:
                res += left * power + right + 1
            else:
                res += (left + 1) * power
        return res

    return count_digit(num2, digit) - count_digit(num1 - 1, digit)
      
int count_digit(long long n, int d) {
    if (n < 0) return 0;
    int res = 0;
    string str_n = to_string(n);
    int length = str_n.size();
    for (int pos = 0; pos < length; ++pos) {
        long long power = 1;
        for (int i = 0; i < length - pos - 1; ++i) power *= 10;
        int current_digit = str_n[pos] - '0';
        int left = (pos > 0) ? stoi(str_n.substr(0, pos)) : 0;
        int right = (pos < length - 1) ? stoi(str_n.substr(pos + 1)) : 0;

        if (d == 0 && pos == 0) continue;
        if (current_digit < d) {
            res += left * power;
        } else if (current_digit == d) {
            res += left * power + right + 1;
        } else {
            res += (left + 1) * power;
        }
    }
    return res;
}

int countDigitOccurrences(int num1, int num2, int digit) {
    return count_digit(num2, digit) - count_digit(num1 - 1, digit);
}
      
public class Solution {
    private int countDigit(long n, int d) {
        if (n < 0) return 0;
        int res = 0;
        String str = Long.toString(n);
        int length = str.length();
        for (int pos = 0; pos < length; ++pos) {
            long power = (long)Math.pow(10, length - pos - 1);
            int currentDigit = str.charAt(pos) - '0';
            int left = (pos > 0) ? Integer.parseInt(str.substring(0, pos)) : 0;
            int right = (pos < length - 1) ? Integer.parseInt(str.substring(pos + 1)) : 0;

            if (d == 0 && pos == 0) continue;
            if (currentDigit < d) {
                res += left * power;
            } else if (currentDigit == d) {
                res += left * power + right + 1;
            } else {
                res += (left + 1) * power;
            }
        }
        return res;
    }

    public int countDigitOccurrences(int num1, int num2, int digit) {
        return countDigit(num2, digit) - countDigit(num1 - 1, digit);
    }
}
      
function countDigitOccurrences(num1, num2, digit) {
    function count_digit(n, d) {
        if (n < 0) return 0;
        let res = 0;
        let str_n = n.toString();
        let length = str_n.length;
        for (let pos = 0; pos < length; ++pos) {
            let power = Math.pow(10, length - pos - 1);
            let current_digit = parseInt(str_n[pos]);
            let left = pos > 0 ? parseInt(str_n.slice(0, pos)) : 0;
            let right = pos < length - 1 ? parseInt(str_n.slice(pos + 1)) : 0;

            if (d === 0 && pos === 0) continue;
            if (current_digit < d) {
                res += left * power;
            } else if (current_digit === d) {
                res += left * power + right + 1;
            } else {
                res += (left + 1) * power;
            }
        }
        return res;
    }
    return count_digit(num2, digit) - count_digit(num1 - 1, digit);
}