每日一题做题记录,参考官方和三叶的题解 |
题目要求
思路
对于阶乘来讲,尾巴的零是由展开式里 5 5 5的数量决定的。因为只有 2 × 5 2\times 5 2×5可以得到 10 10 10,造出尾巴里的零,那很明显 2 2 2的数量要比 5 5 5多,所以只要统计 5 5 5的数量就好了。
至此,题目变成了统计质因数。拆分质因数最终会获得一个类似于…(2*3)…(2*5)…
的算式,在后面数越来越大的时候会出现…(2*3*3)…(2*5*5)…
等类似的情况,甚至于(2*5*5*5)
等。也就是说每隔5个数会有一个
5
5
5需要统计,每个25个数会有两个
5
5
5需要统计(多一个
5
5
5需要统计),每125个数会有三个
5
5
5需要统计(再多一个)……把它们加起来就是答案。
在 [ 1 , n ] [1,n] [1,n]的范围内:
- p p p的倍数有 c n t 1 = ⌊ n p ⌋ cnt_1=\lfloor \frac{n}{p} \rfloor cnt1=⌊pn⌋个;
- p 2 p^2 p2的倍数有 c n t 2 = ⌊ n p 2 ⌋ cnt_2=\lfloor \frac{n}{p^2} \rfloor cnt2=⌊p2n⌋个;
- ……
越往下数其实是越少的,很明显是 p 2 p^2 p2的倍数也会是 p p p的倍数,避免重复统计,只加多出来的 p p p就可以, [ 1 , n ] [1,n] [1,n]中质因子 p p p的个数为 ∑ k = 1 ∞ ⌊ n p k ⌋ \sum_{k=1}^\infty\lfloor \frac{n}{p^k}\rfloor ∑k=1∞⌊pkn⌋。
代码实现起来其实就直接递归了,两种语言是一样的😂,就顺手写了下python。
Java
class Solution {
public int trailingZeroes(int n) {
return n == 0 ? 0 : n / 5 + trailingZeroes(n / 5);
}
}
- 时间复杂度: O ( log n ) O(\log n) O(logn)
- 空间复杂度: O ( 1 ) O(1) O(1),忽略递归的额外空间开销
C++
class Solution {
public:
int trailingZeroes(int n) {
return n == 0 ? 0 : n / 5 + trailingZeroes(n / 5);
}
};
- 时间复杂度: O ( log n ) O(\log n) O(logn)
- 空间复杂度: O ( 1 ) O(1) O(1)
Python
class Solution:
def trailingZeroes(self, n: int) -> int:
return n // 5 + self.trailingZeroes(n // 5) if n else 0
- 时间复杂度: O ( log n ) O(\log n) O(logn)
- 空间复杂度: O ( 1 ) O(1) O(1)
总结
一道纯靠数学思路的题目,源头就是找因子 5 5 5的个数,初拿到手还在想怎么快速判断那么多个数都是 5 5 5的多少次,然后反应过来一路除下去加起来就好了。
欢迎指正与讨论! |