核心思路与数学原理
本题要求找到仅由数字 1 组成的最小正整数 N,使得 N 能被给定的正整数 K 整除,并返回 N 的位数。若不存在这样的 N,则返回 -1 。
1. 无解情况判定
若 K 是偶数(即 K % 2 == 0)或 K是 5 的倍数(即 K % 5 == 0),则不存在仅由 1 组成且能被 K 整除的数。这是因为任何仅由 1 组成的数(如 1, 11, 111...)其个位永远是 1,而能被 2 或 5 整除的数个位必须是 0, 2, 4, 5, 6, 8,因此直接返回 -1 。
2. 鸽巢原理(抽屉原理)优化
若 K 与 10 互质(即非 2 和 5 的倍数),解一定存在。我们不需要构造巨大的整数 N,而是通过模拟除法过程计算余数。
设 R_i 为由 i 个 1 组成的数对 K 取模的结果。递推公式为:
R_i = (R_{i-1} \\times 10 + 1) \\pmod K
其中 R_0 = 0。
根据鸽巢原理,余数的可能取值范围是 \[0, K1\]。如果在 K 次迭代内余数仍未变为 0,则必然出现重复的余数,意味着进入了无限循环,此时可判定无解(但在本题数学特性下,只要 K 不含因子 2 和 5,必在 K 次内找到解)。因此,循环次数上限为 K。
3. 避免溢出
直接使用 long long 存储全 1 数会在位数较多时溢出。必须在每一步计算后立即对 K 取模,保持数值始终小于 K 。
C++ 代码实现
cpp
#include <iostream>
#include <unordered_set>
class Solution {
public:
int smallestRepunitDivByK(int k) {
// 若 k 是偶数或 5的倍数,个位为 1 的数永远无法被整除,直接返回 -1
if (k % 2 == 0 || k % 5 == 0) {
return -1;
}
int remainder = 0; // 当前余数
// 根据鸽巢原理,最多尝试 k 次即可找到循环节或解 for (int length = 1; length <= k; ++length) {
// 核心递推公式:新余数 = (旧余数 * 10 + 1) % k
// 此操作避免了大数溢出,始终保持 remainder < k
remainder = (remainder * 10 + 1) % k;
// 若余数为 0,说明当前的 length 即为所求的最小位数
if (remainder == 0) {
return length;
}
}
// 理论上若 k 不含 2 和 5 因子,必在上述循环内返回;此处为防御性编程
return -1;
}
};
// 示例主函数用于本地测试
int main() {
Solution sol;
int k1 = 1;
int k2 = 2;
int k3 = 3;
int k4 = 7;
std::cout << "K=" << k1 << ", Length: " << sol.smallestRepunitDivByK(k1) << std::endl; // 输出 1 (1%1==0)
std::cout << "K=" << k2 << ", Length: " << sol.smallestRepunitDivByK(k2) << std::endl; // 输出 -1 (偶数)
std::cout << "K=" << k3 << ", Length: " << sol.smallestRepunitDivByK(k3) << std::endl; // 输出 3 (111%3==0)
std::cout << "K=" << k4 << ", Length: " << sol.smallestRepunitDivByK(k4) << std::endl; // 输出 6 (111111%7==0)
return 0;
}
算法复杂度分析
| 指标 | 分析结果 | 说明 |
|---|---|---|
| 时间复杂度 | O(K) | 最坏情况下需要遍历 K 次余数状态,每次计算为 O(1) 。 |
| 空间复杂度 | O(1) | 仅使用了几个整型变量存储余数和计数器,未使用额外数组或哈希表(利用数学性质隐式去重)。 |
| 数据范围 | 支持 K \\le 10\^5 | 由于避免了大数运算,该算法能高效处理 LeetCode 规定的 K 的最大值。 |
关键步骤解析
- 预处理判断 :首先检查
k % 2 == 0或k % 5 == 0。这是基于数论性质的快速剪枝,因为 10x + 1 形式的数不可能被 2 或 5 整除 。 - 模运算递推 :利用
(remainder * 10 + 1) % k模拟在末尾添加数字1的过程。例如,从1到11,数学上是 1 \\times 10 + 1;从11到111,是 11 \\times 10 + 1。通过对 K 取模,我们将数值控制在整数范围内 。 - 循环终止条件:循环上限设为 K。如果在 K 次内没有找到余数为 0 的情况,根据抽屉原理,余数必然重复,后续将进入死循环,故可判定无解(尽管对于互质的 K,解必然存在)。