力扣1015C++解法技巧

核心思路与数学原理

本题要求找到仅由数字 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 的最大值。

关键步骤解析

  1. 预处理判断 :首先检查 k % 2 == 0 或 k % 5 == 0。这是基于数论性质的快速剪枝,因为 10x + 1 形式的数不可能被 2 或 5 整除 。
  2. 模运算递推 :利用 (remainder * 10 + 1) % k 模拟在末尾添加数字 1的过程。例如,从 1 到 11,数学上是 1 \\times 10 + 1;从 11 到 111,是 11 \\times 10 + 1。通过对 K 取模,我们将数值控制在整数范围内 。
  3. 循环终止条件:循环上限设为 K。如果在 K 次内没有找到余数为 0 的情况,根据抽屉原理,余数必然重复,后续将进入死循环,故可判定无解(尽管对于互质的 K,解必然存在)。

参考来源

相关推荐
lsc_blog2 小时前
听到优化风声,先准备能由自己掌控的事
数据库·分布式·中间件·职场和发展·重构
通信小呆呆4 小时前
电子战抗干扰技术:当“不被干扰”成为一种系统
算法·通信·mimo·isac
老歌老听老掉牙4 小时前
案例 7:二维 Rosenbrock 函数最小值(香蕉函数)
c++·pso
笑容掩饰我的失落4 小时前
区块链从小白到精通(八):从零实现一个标准 ERC-20 代币(完整可运行代码)
算法·区块链·php·智能合约·哈希算法
Severus_black4 小时前
【C++初阶】Stack、Queue算法题大练兵!
c语言·c++·算法·stl
2601_967979127 小时前
机器学习必修课:经典算法与Python实战
python·算法·机器学习·实战·课程
程序猿打工仔7 小时前
计算机基础(C语言)考编、专技岗
c语言·开发语言·算法
通信小呆呆7 小时前
电子战干扰技术:从“蛮力压制”到“精准欺骗”,再到“认知博弈”
算法·通信·isac
老师我太想进步了20268 小时前
【数据结构-6】算法美学之单链表(1)
数据结构·算法
xiaoye-duck9 小时前
《C++ 项目:手写大模型接入 SDK》DataManager数据管理模块:SQLite持久化存储
开发语言·数据库·c++·sqlite