Self Dividing Numbers: Check Every Digit
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.
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
1%1=0Only digit is 1, divides evenly.
Step 2: Check 2
2%2=0Only digit is 2, divides evenly.
Step 3: Check 3
3%3=0Only digit is 3, divides evenly.
Step 4: Check 4
4%4=0Only digit is 4, divides evenly.
Step 5: Check 5
5%5=0Only digit is 5, divides evenly.
Step 6: Check 6
6%6=0Only digit is 6, divides evenly.
Step 7: Check 7
7%7=0Only digit is 7, divides evenly.
Step 8: Check 8
8%8=0Only digit is 8, divides evenly.
Step 9: Check 9
9%9=0Only digit is 9, divides evenly.
Step 10: Check 10
digit 0Contains 0. Cannot divide by zero.
Step 11: Check 11
11%1=0, 11%1=0Both digits are 1, divides evenly.
Step 12: Check 12
12%1=0, 12%2=012 is divisible by both 1 and 2.
Step 13: Check 13
13%1=0, 13%3!=013 is not divisible by 3.
Step 14: Check 14
14%1=0, 14%4!=014 is not divisible by 4.
Step 15: Check 15
15%1=0, 15%5=015 is divisible by both 1 and 5.
Step 16: Check 16
16%1=0, 16%6!=016 is not divisible by 6.
Step 17: Check 17
17%1=0, 17%7!=017 is not divisible by 7.
Step 18: Check 18
18%1=0, 18%8!=018 is not divisible by 8.
Step 19: Check 19
19%1=0, 19%9!=019 is not divisible by 9.
Step 20: Check 20
digit 0Contains 0. Cannot divide by zero.
Step 21: Check 21
21%2!=021 is not divisible by 2.
Step 22: Check 22
22%2=0, 22%2=0Both digits are 2, divides evenly.
Result
[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
| Metric | Value |
|---|---|
| Time | O((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. |
| Space | O(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