Check each number in a range by extracting its digits and testing whether every digit divides the original number.
Problem Restatement
We are given two integers, left and right.
We need to return all self-dividing numbers in the inclusive range:
[left, right]A self-dividing number is divisible by every digit it contains.
For example, 128 is self-dividing because:
128 % 1 == 0
128 % 2 == 0
128 % 8 == 0A self-dividing number cannot contain the digit 0, because division by zero is not allowed.
The official constraint is:
1 <= left <= right <= 10^4Input and Output
| Item | Meaning |
|---|---|
| Input | Two integers left and right |
| Output | A list of all self-dividing numbers in the range |
| Range | Both left and right are included |
| Invalid digit | Any number containing 0 is not self-dividing |
Example function shape:
def selfDividingNumbers(left: int, right: int) -> list[int]:
...Examples
Example 1
left = 1
right = 22The self-dividing numbers are:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 15, 22]Numbers from 1 to 9 are self-dividing because each number has one digit and is divisible by itself.
10 is not self-dividing because it contains 0.
12 is self-dividing because:
12 % 1 == 0
12 % 2 == 015 is self-dividing because:
15 % 1 == 0
15 % 5 == 022 is self-dividing because:
22 % 2 == 0
22 % 2 == 0Example 2
left = 47
right = 85The answer is:
[48, 55, 66, 77]48 works because:
48 % 4 == 0
48 % 8 == 055 works because:
55 % 5 == 0
55 % 5 == 0First Thought: Check Every Number
The constraints are small. Since right <= 10^4, we can check every number in the range.
For each number, we inspect its digits.
A number is valid if:
- None of its digits is
0. - Every digit divides the original number exactly.
So the main task is writing a helper function:
is_self_dividing(x)This function returns True if x is self-dividing, and False otherwise.
Key Insight
When checking a number, we must keep the original number unchanged.
For example, suppose:
x = 128We need to test:
128 % 8
128 % 2
128 % 1But if we extract digits using division, the working value changes:
128 -> 12 -> 1 -> 0So we use two variables:
original = x
current = xcurrent is used to extract digits.
original is used for divisibility checks.
To get the last digit:
digit = current % 10To remove the last digit:
current //= 10Algorithm
Create an empty answer list.
For every number x from left to right:
- Check whether
xis self-dividing. - If it is, append it to the answer list.
To check whether x is self-dividing:
- Store
original = x. - While
x > 0:- Extract the last digit with
x % 10. - If the digit is
0, returnFalse. - If
original % digit != 0, returnFalse. - Remove the last digit with
x //= 10.
- Extract the last digit with
- Return
True.
Correctness
The helper function examines every digit of the candidate number.
At each step, x % 10 gives the current last digit, and x //= 10 removes that digit. Repeating this process eventually visits all digits exactly once.
If any digit is 0, the helper returns False. This is correct because a self-dividing number is not allowed to contain zero.
If any digit does not divide the original number, the helper returns False. This is correct because a self-dividing number must be divisible by every digit it contains.
If the loop finishes, then every digit was nonzero and every digit divided the original number. Therefore the number is self-dividing.
The outer loop checks every integer in the inclusive range [left, right]. It appends exactly the numbers accepted by the helper. Therefore the returned list contains exactly all self-dividing numbers in the required range.
Complexity
Let:
n = right - left + 1Let d be the maximum number of digits among numbers in the range.
| Metric | Value | Why |
|---|---|---|
| Time | O(n * d) | We check each number and scan its digits |
| Space | O(1) extra | The helper uses only a few variables |
The returned list is not counted as extra working space.
Since right <= 10^4, d is at most 5.
Implementation
class Solution:
def selfDividingNumbers(self, left: int, right: int) -> list[int]:
def is_self_dividing(x: int) -> bool:
original = x
while x > 0:
digit = x % 10
if digit == 0:
return False
if original % digit != 0:
return False
x //= 10
return True
answer = []
for x in range(left, right + 1):
if is_self_dividing(x):
answer.append(x)
return answerCode Explanation
The helper function starts by saving the unchanged number:
original = xThis matters because x will be reduced while we extract digits.
The loop processes one digit at a time:
while x > 0:The last digit is:
digit = x % 10If the digit is zero, the number is invalid:
if digit == 0:
return FalseOtherwise, we check divisibility using the original number:
if original % digit != 0:
return FalseThen we remove the digit:
x //= 10If all digits pass, the number is self-dividing:
return TrueThe outer loop tests every number in the range:
for x in range(left, right + 1):The + 1 is needed because Python ranges exclude the right endpoint.
Testing
def run_tests():
s = Solution()
assert s.selfDividingNumbers(1, 22) == [
1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 15, 22
]
assert s.selfDividingNumbers(47, 85) == [48, 55, 66, 77]
assert s.selfDividingNumbers(10, 10) == []
assert s.selfDividingNumbers(11, 11) == [11]
assert s.selfDividingNumbers(100, 105) == []
print("all tests passed")
run_tests()| Test | Why |
|---|---|
1 to 22 | Official-style basic range |
47 to 85 | Larger two-digit range |
10 to 10 | Contains zero |
11 to 11 | Single valid number |
100 to 105 | All contain zero or fail divisibility |