LC 728. 自除数

题目描述

这是 LeetCode 上的 728. 自除数 ,难度为 简单

自除数是指可以被它包含的每一位数整除的数。

  • 例如,$128$ 是一个 自除数 ,因为 128 % 1 == 0128 % 2 == 0128 % 8 == 0

自除数不允许包含 $0$ 。

给定两个整数 leftright ,返回一个列表,列表的元素是范围 $[left, right]$ 内所有的 自除数 。

示例 1:

1
2
3
输入:left = 1, right = 22

输出:[1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 15, 22]

示例 2:
1
2
3
输入:left = 47, right = 85

输出:[48,55,66,77]

提示:

  • $1 <= left <= right <= 10^4$

模拟

根据题意进行模拟即可。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
public List<Integer> selfDividingNumbers(int left, int right) {
List<Integer> ans = new ArrayList<>();
out:for (int i = left; i <= right; i++) {
int cur = i;
while (cur != 0) {
int t = cur % 10;
if (t == 0 || i % t != 0) continue out;
cur /= 10;
}
ans.add(i);
}
return ans;
}
}

-

1
2
3
4
5
6
7
8
9
10
11
class Solution:
def selfDividingNumbers(self, left: int, right: int) -> List[int]:
ans = []
for i in range(left, right + 1):
cur, ok = i, True
while cur and ok:
ok = not ((t := cur % 10) == 0 or i % t != 0)
cur //= 10
if ok:
ans.append(i)
return ans
  • 时间复杂度:令 $n = right - left + 1$,复杂度为 $O(n * \log{right})$
  • 空间复杂度:$O(1)$

打表 + 二分

利用数据范围只有 $1e4$,我们可以打表预处理出所有的自除数,通过二分找到 $[left, right]$ 范围内的最小自除数,再从前往后找到所有合法的自除数。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution {
static List<Integer> list = new ArrayList<>();
static {
out:for (int i = 1; i <= 10000; i++) {
int cur = i;
while (cur != 0) {
int u = cur % 10;
if (u == 0 || i % u != 0) continue out;
cur /= 10;
}
list.add(i);
}
}
public List<Integer> selfDividingNumbers(int left, int right) {
List<Integer> ans = new ArrayList<>();
int l = 0, r = list.size() - 1;
while (l < r) {
int mid = l + r >> 1;
if (list.get(mid) >= left) r = mid;
else l = mid + 1;
}
while (r < list.size() && list.get(r) <= right) ans.add(list.get(r++));
return ans;
}
}

  • 时间复杂度:令 $m$ 为范围在 $[left, right]$ 之间的自除数的数量,$n = right - left + 1$。复杂度为$O(\log{m} + n)$,对于本题,$m$ 上界为 $339$
  • 空间复杂度:$O(m)$

打表 + 哈希表

在打表预处理了所有范围内的自除数的基础上,我们可以干脆将索引也预处理出来,从而避免二分操作。

其中 $hash[x]$ 的含义为值不超过 $x$ 的最大自除数在 list 中的下标。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
static List<Integer> list = new ArrayList<>();
static int[] hash = new int[10010];
static {
for (int i = 1; i <= 10000; i++) {
int cur = i;
boolean ok = true;
while (cur != 0 && ok) {
int u = cur % 10;
if (u == 0 || i % u != 0) ok = false;
cur /= 10;
}
if (ok) list.add(i);
hash[i] = list.size() - 1;
}
}
public List<Integer> selfDividingNumbers(int left, int right) {
List<Integer> ans = new ArrayList<>();
int idx = list.get(hash[left]) == left ? hash[left] : hash[left] + 1;
while (idx < list.size() && list.get(idx) <= right) ans.add(list.get(idx++));
return ans;
}
}

  • 时间复杂度:$n = right - left + 1$。复杂度为$O(n)$
  • 空间复杂度:$O(C)$

最后

这是我们「刷穿 LeetCode」系列文章的第 No.726 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。

在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。

为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode

在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。