携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第28天,点击查看活动详情
f(x) 是 x! 末尾是 0 的数量。回想一下 x! = 1 * 2 * 3 * ... * x,且 0! = 1 。
- 例如,
f(3) = 0,因为3! = 6的末尾没有 0 ;而f(11) = 2,因为11!= 39916800末端有 2 个 0 。
给定 k,找出返回能满足 f(x) = k 的非负整数 x 的数量。
示例 1:
输入:k = 0
输出:5
解释:0!, 1!, 2!, 3!, 和 4! 均符合 k = 0 的条件。
示例 2:
输入:k = 5
输出:0
解释:没有匹配到这样的 x!,符合 k = 5 的条件。
示例 3:
输入: k = 3
输出: 5
二分查找
n! 尾零的数量即为 中因子 的个数,而 ,因此转换成求 中质因子 的个数和质因子 的个数的较小值。
由于质因子 5 的个数不会大于质因子 2 的个数(具体证明见方法二),我们可以仅考虑质因子 5 的个数。
而 n! 中质因子 5 的个数等于 [1,n] 的每个数的质因子 5 的个数之和,我们可以通过遍历 [1,n] 的所有 5 的倍数求出。
优化计算
换一个角度考虑 [1,n] 中质因子 p 的个数。
[1,n] 中 p 的倍数有 个,这些数至少贡献出了 个质因子 p。 的倍数有 个,由于这些数已经是 p 的倍数了,为了不重复统计 p 的个数,我们仅考虑额外贡献的质因子个数,即这些数额外贡献了至少 个质因子 p。
代码实现
首先我们令 为 末尾零的个数。
记 表示 末尾零的个数不小于 的最小数,那么题目等价于求解 。
由于 为单调不减函数,因此 和 可以通过「二分查找」来求解。
又因为
得
所以当二分求解 时,我们可以将二分的初始右边界 r 设置为 5x。
var preimageSizeFZF = function(k) {
return help(k + 1) - help(k);
}
const help = (k) => {
let r = 5 * k;
let l = 0;
while (l <= r) {
const mid = Math.floor((l + r) / 2);
if (zeta(mid) < k) {
l = mid + 1;
} else {
r = mid - 1;
}
}
return r + 1;
}
const zeta = (x) => {
let res = 0;
while (x != 0) {
res += Math.floor(x / 5);
x = Math.floor(x / 5);
}
return res;
};