Skip to content
← All posts

Self Dividing Numbers: Check Every Digit

4 min read
leetcodeproblemeasymath

LeetCode 728. Self Dividing Numbers asks you to find all "self-dividing" numbers within a given range. A self-dividing number is divisible by every digit it contains, and it must not contain the digit 0 (since division by zero is undefined). This is a clean digit manipulation problem with no tricks.

The problem

Given two integers left and right, return a list of all self-dividing numbers in the range [left, right].

A number is self-dividing if every digit of the number divides the number evenly. For example, 128 is self-dividing because 128 % 1 == 0, 128 % 2 == 0, and 128 % 8 == 0. The number 102 is not self-dividing because it contains 0.

Is the number self-dividing?Check each digit divides the number evenly1281128%1=02128%2=08128%8=0✓ yes1021102%1=00zero!2102%2=0✗ noDivides evenlyFails (zero or no remainder)
128 is self-dividing because 1, 2, and 8 all divide 128. 102 is not because it contains a 0.

The approach

The logic is simple. For each number in the range, extract every digit and check two things: the digit is not zero, and the number is divisible by that digit. If all digits pass both checks, the number is self-dividing.

You can extract digits either by converting the number to a string (and iterating over characters) or by using modular arithmetic (n % 10 to get the last digit, n // 10 to remove it). Both approaches work fine here. The string approach is slightly more readable, while the mod approach avoids string allocation.

Here is the string-based solution:

def self_dividing_numbers(left: int, right: int) -> list[int]:
    result = []
    for num in range(left, right + 1):
        if all(int(d) != 0 and num % int(d) == 0 for d in str(num)):
            result.append(num)
    return result

And here is the modular arithmetic version:

def self_dividing_numbers(left: int, right: int) -> list[int]:
    def is_self_dividing(n: int) -> bool:
        original = n
        while n > 0:
            digit = n % 10
            if digit == 0 or original % digit != 0:
                return False
            n //= 10
        return True

    return [n for n in range(left, right + 1) if is_self_dividing(n)]

Both solutions iterate through the range and check each number independently. The helper function (or generator expression) keeps the logic clean and testable.

Step-by-step walkthrough

Let's trace through the range [1, 22] and check each number.

Step 1: Check 1

number =1
digit checks:1%1=0
Self-dividing

Only digit is 1, divides evenly.

Step 2: Check 2

number =2
digit checks:2%2=0
Self-dividing

Only digit is 2, divides evenly.

Step 3: Check 3

number =3
digit checks:3%3=0
Self-dividing

Only digit is 3, divides evenly.

Step 4: Check 4

number =4
digit checks:4%4=0
Self-dividing

Only digit is 4, divides evenly.

Step 5: Check 5

number =5
digit checks:5%5=0
Self-dividing

Only digit is 5, divides evenly.

Step 6: Check 6

number =6
digit checks:6%6=0
Self-dividing

Only digit is 6, divides evenly.

Step 7: Check 7

number =7
digit checks:7%7=0
Self-dividing

Only digit is 7, divides evenly.

Step 8: Check 8

number =8
digit checks:8%8=0
Self-dividing

Only digit is 8, divides evenly.

Step 9: Check 9

number =9
digit checks:9%9=0
Self-dividing

Only digit is 9, divides evenly.

Step 10: Check 10

number =10
digit checks:digit 0
Not self-dividing

Contains 0. Cannot divide by zero.

Step 11: Check 11

number =11
digit checks:11%1=0, 11%1=0
Self-dividing

Both digits are 1, divides evenly.

Step 12: Check 12

number =12
digit checks:12%1=0, 12%2=0
Self-dividing

12 is divisible by both 1 and 2.

Step 13: Check 13

number =13
digit checks:13%1=0, 13%3!=0
Not self-dividing

13 is not divisible by 3.

Step 14: Check 14

number =14
digit checks:14%1=0, 14%4!=0
Not self-dividing

14 is not divisible by 4.

Step 15: Check 15

number =15
digit checks:15%1=0, 15%5=0
Self-dividing

15 is divisible by both 1 and 5.

Step 16: Check 16

number =16
digit checks:16%1=0, 16%6!=0
Not self-dividing

16 is not divisible by 6.

Step 17: Check 17

number =17
digit checks:17%1=0, 17%7!=0
Not self-dividing

17 is not divisible by 7.

Step 18: Check 18

number =18
digit checks:18%1=0, 18%8!=0
Not self-dividing

18 is not divisible by 8.

Step 19: Check 19

number =19
digit checks:19%1=0, 19%9!=0
Not self-dividing

19 is not divisible by 9.

Step 20: Check 20

number =20
digit checks:digit 0
Not self-dividing

Contains 0. Cannot divide by zero.

Step 21: Check 21

number =21
digit checks:21%2!=0
Not self-dividing

21 is not divisible by 2.

Step 22: Check 22

number =22
digit checks:22%2=0, 22%2=0
Self-dividing

Both digits are 2, divides evenly.

Result

output =[1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 15, 22]

13 numbers from 1 to 22 are self-dividing.

Single-digit numbers (1 through 9) are always self-dividing since each number trivially divides itself. The interesting cases start at 10, where two-digit numbers introduce the possibility of zero digits or failed divisibility checks.

Complexity analysis

MetricValue
TimeO((right - left) * d), where d is the maximum number of digits in any number in the range. For each number you check up to d digits.
SpaceO(1) extra space beyond the output list. Each number is checked independently with no auxiliary data structures.

Since the digit count d is at most 7 for numbers up to 10^7 (the constraint), the per-number cost is effectively constant. The overall runtime scales linearly with the size of the range.

The building blocks

This problem decomposes into two reusable pieces that appear across many LeetCode problems.

1. Digit extraction

Pulling individual digits out of a number, either via string conversion or repeated modulo and division. This is the same micro-pattern you use in Palindrome Number, Happy Number, Reverse Integer, and Add Digits.

while n > 0:
    digit = n % 10
    n //= 10

2. Range filtering with a predicate

Iterating over a range and collecting elements that satisfy a condition. This is essentially filter with a custom check. You see the same structure in Fizz Buzz (classify each number) and Count Primes (test each number).

[n for n in range(left, right + 1) if predicate(n)]

On CodeBricks, you drill each building block separately. When they show up combined in a problem like Self Dividing Numbers, you recognize both pieces instantly.

Edge cases

  • Single-digit numbers (1 through 9): Always self-dividing, since each number divides itself.
  • Numbers containing 0: Immediately rejected. You cannot divide by zero, so 10, 20, 30, 102, and similar numbers all fail.
  • left equals right: The range contains exactly one number. Your loop still works correctly.
  • Numbers like 111 or 222: Every digit is the same, so you only need to check divisibility by that one digit. These pass quickly.

From understanding to recall

Reading through this solution takes a minute, and the logic clicks quickly. But being able to reproduce it under pressure, two weeks from now, requires more than understanding.

That is what spaced repetition solves. CodeBricks breaks Self Dividing Numbers into its building blocks (digit extraction, range filtering) and drills them at increasing intervals. You type each piece from scratch, not from memory of the full solution, but from understanding the pattern. After a few review cycles, pulling digits out of a number and checking divisibility becomes automatic.

Related posts

  • Palindrome Number - Another digit manipulation problem where you extract and compare digits without string conversion
  • Happy Number - Digit extraction combined with cycle detection, using the same modular arithmetic building block
  • Fizz Buzz - Range iteration with divisibility checks, the same "classify each number" structure used here