LeetCode 1015. 可被 K 整除的最小整数 - 数学推导与鸽巢原理

这道题目与其说是一道编程题,不如说是一道纯粹的数学题。它考察了同余运算(Modulo Arithmetic)和鸽巢原理(Pigeonhole Principle)。

1. 题目核心问题

我们需要找到一个仅由数字 1 组成的整数 N(如 1, 11, 111...),使得 N % K == 0。我们需要返回这个 N 的长度。

2. 数学分析

2.1 排除不可能的情况

首先,观察仅由 1 组成的数字:1, 11, 111, 1111...

这些数字的个位数永远是 1

  • 如果 K 是偶数(能被 2 整除),那么 K 的倍数必定是偶数,个位只能是 0, 2, 4, 6, 8。
  • 如果 K 是 5 的倍数,那么 K 的倍数个位只能是 0 或 5。

因此,如果 K 能被 2 或 5 整除(即 K % 2 == 0K % 5 == 0),那么不可能存在个位是 1 的倍数。直接返回 -1

2.2 大数处理与同余运算

题目中 N 的长度可能非常大,甚至超过 64 位整数的范围,因此我们不能直接计算 N。我们只需要关注 余数

我们定义 NiN_iNi 为长度为 iii 的全 1 数字。

例如:N1=1,N2=11,N3=111N_1 = 1, N_2 = 11, N_3 = 111N1=1,N2=11,N3=111。

它们之间存在递推关系:
Ni=Ni−1×10+1N_{i} = N_{i-1} \times 10 + 1Ni=Ni−1×10+1

根据同余性质:
(a×b+c)(modK)=((a(modK))×b+c)(modK)(a \times b + c) \pmod K = ((a \pmod K) \times b + c) \pmod K(a×b+c)(modK)=((a(modK))×b+c)(modK)

我们可以只维护当前的余数 Ri=Ni(modK)R_i = N_i \pmod KRi=Ni(modK):
Ri=(Ri−1×10+1)(modK)R_i = (R_{i-1} \times 10 + 1) \pmod KRi=(Ri−1×10+1)(modK)

初始状态 R1=1(modK)R_1 = 1 \pmod KR1=1(modK)。

2.3 为什么最多循环 K 次?(鸽巢原理)

如果在计算过程中,余数 RiR_iRi 变为 0,说明找到了答案,长度为 iii。

如果一直没找到 0 呢?会不会无限循环?

余数的取值范围是 [0,K−1][0, K-1][0,K−1],共有 KKK 种可能。

如果我们计算了 KKK 次,生成了 KKK 个余数:

  1. 如果其中包含 0,则已找到答案。
  2. 如果其中不包含 0,那么这 KKK 个余数分布在 [1,K−1][1, K-1][1,K−1] 这 K−1K-1K−1 个"鸽巢"中。根据鸽巢原理,必然有两个余数是相同的。

一旦出现重复的余数,序列就会开始循环,永远不会出现 0(因为如果能出现 0,早在循环之前或循环的第一次就出现了)。

结论 :如果 K 不含因子 2 和 5,我们最多只需要尝试 K 次。如果 K 次内没有找到 0,则无解(实际上对于互质的情况,一定有解)。

3. 算法流程

  1. 检查 K % 2 == 0K % 5 == 0,若是则返回 -1。
  2. 初始化余数 remainder = 0
  3. 循环 i 从 1 到 K
    • 更新余数:remainder = (remainder * 10 + 1) % K
    • 如果 remainder == 0,返回当前长度 i
  4. 如果循环结束仍未找到,返回 -1(理论上这一步不会到达,除非 K 有 2 或 5 的因子)。

4. 代码实现 (Go)

go 复制代码
func smallestRepunitDivByK(k int) int {
    // 1. 排除 2 和 5 的倍数
    if k%2 == 0 || k%5 == 0 {
        return -1
    }

    // 2. 模拟除法过程
    remainder := 0
    for i := 1; i <= k; i++ {
        remainder = (remainder*10 + 1) % k
        if remainder == 0 {
            return i
        }
    }

    return -1
}

5. 复杂度分析

  • 时间复杂度 : O(K)O(K)O(K)。最坏情况下我们需要循环 KKK 次。
  • 空间复杂度 : O(1)O(1)O(1)。只需要存储当前的余数。
相关推荐
海清河晏1111 小时前
数据结构 | 单循环链表
数据结构·算法·链表
wuweijianlove5 小时前
算法性能的渐近与非渐近行为对比的技术4
算法
_dindong5 小时前
cf1091div2 C.Grid Covering(数论)
c++·算法
AI成长日志5 小时前
【Agentic RL】1.1 什么是Agentic RL:从传统RL到智能体学习
人工智能·学习·算法
黎阳之光6 小时前
黎阳之光:视频孪生领跑者,铸就中国数字科技全球竞争力
大数据·人工智能·算法·安全·数字孪生
skywalker_116 小时前
力扣hot100-3(最长连续序列),4(移动零)
数据结构·算法·leetcode
6Hzlia6 小时前
【Hot 100 刷题计划】 LeetCode 17. 电话号码的字母组合 | C++ 回溯算法经典模板
c++·算法·leetcode
wfbcg6 小时前
每日算法练习:LeetCode 209. 长度最小的子数组 ✅
算法·leetcode·职场和发展
_日拱一卒6 小时前
LeetCode:除了自身以外数组的乘积
数据结构·算法·leetcode
计算机安禾7 小时前
【数据结构与算法】第36篇:排序大总结:稳定性、时间复杂度与适用场景
c语言·数据结构·c++·算法·链表·线性回归·visual studio