题目
Given an integer n, return the number of trailing zeroes in n!.
Example 1:
Input: 3 Output: 0 Explanation: 3! = 6, no trailing zero. Example 2:
Input: 5 Output: 1 Explanation: 5! = 120, one trailing zero. Note:
Your solution should be in logarithmic time complexity.
想记录一下这道题的原因还是今天恰好碰到了一道笔试题,问到的是一串数字的积,它的末尾有多少个0,看到这题就想到了LeetCode的这题,顺便记录一下。
题意很简单,就是给一个数n,让你找出n的阶乘末尾有几个0,比如n=3,3的阶乘是6,没有0,再比如n=5,阶乘是120,有一个0.
题解
解法一:暴力(会超时)
最简单的想法就是把阶乘求出来,然后再依次看它末尾有几个0,但是n如果很大,这个数能不能表示出来都是问题,然后再求末尾0肯定超时。而且题目要求是在对数时间范围内求解。
解法二:找规律
想一下要使得末尾出现0,那么意味着是10的倍数,也就意味着需要知道这些数的乘积中有多少个可以拎出来的10,而10又是可以拆成2乘5,所以我们可以把问题转换成这些乘数中能质因数分解成多少个2和5的乘积对。
进一步,我们知道只要是大于等于2的数,它的阶乘中拆成的2的数量肯定是比5多的,因为每一个偶数就可以拆一个2出来,但是5需要5的倍数来拆,所以这个问题又进一步化成求乘数中有多少个5
那么最简单的做法,就是对每一个数,看是不是5的倍数,是就加一,最后加出来的结果就是0的个数。但是注意还有一些特殊的数字,比如25,125这种5的乘方,它们提供的5的数量不止普通的5的个数,所以需要单独考虑,比如5的非乘方倍数就提供一个5,而5的平方25的倍数就提供2个5,比如25,50,75…,再比如5的立方就提供3个5,比如125,625…
这样一看,我们的思路就清晰了,只需要在不超过n的范围内,对5的1次,2次,…n次的平方的倍数分别加上其对应提供的5的数量就是题目所需要的答案。
进一步思考,由于5的2次方就一定是5的1次方的倍数,而5的3次方一定是5的2次方的倍数,也就是说我们在算5的一次方提供的5的数量的时候,同时把2次方,3次方…n次方的那一份5也算进去了,接着我们在算5的二次方的提供的5的数量的时候,由于已经在一次方算了一次,原本应该需要乘2的这里只需要乘1,类似的,三次方被一次和两次都算了一个5,它原本提供3个5的现在也只需要提供一个5,所以对1,2,3…次方都只需要乘1倍,也就是相当于做加法
一定要理解这段话才能理解下面的代码:
class Solution {
public:
int trailingZeroes(int n) {
int ret = 0;
for (long long i = 5; i <= n; i *= 5) {
ret += n / i; //只需要加上n / i而不用乘以它对应的提供的5的数量,原因上面解释了
}
return ret;
}
};
这个代码每次i都在乘以5,呈指数的上升速度,所以时间复杂度是对数级别,满足题目要求