leetcode793. 阶乘函数后 K 个零(java)

265 篇文章 2 订阅
235 篇文章 0 订阅

阶乘函数后 K 个零

题目描述

难度 - 困难
阶乘函数后 K 个零

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

提示:
0 <= k <= 1e9

在这里插入图片描述

二分法

搜索有多少个n满足trailingZeroes(n) == K,其实就是在问,满足条件的n最小是多少,最大是多少,最大值和最小值一减,就可以算出来有多少个n满足条件了。
那不就是二分查找「搜索左侧边界」和「搜索右侧边界」这两个事儿嘛?

因为二分查找需要给一个搜索区间,也就是上界和下界,上述伪码中n的下界显然是 0,但上界是+inf,这个正无穷应该如何表示出来呢?

首先,数学上的正无穷肯定是无法编程表示出来的,我们一般的方法是用一个非常大的值,大到这个值一定不会被取到。比如说 int 类型的最大值INT_MAX(2^31 - 1,大约 31 亿),还不够的话就 long 类型的最大值LONG_MAX(2^63 - 1,这个值就大到离谱了)。
那么我怎么知道需要多大才能「一定不会被取到」呢?这就需要认真读题,看看题目给的数据范围有多大。

这道题目实际上给了限制,K是在[0,109]区间内的整数,也就是说,trailingZeroes(n)的结果最多可能达到109。
然后我们可以反推,当trailingZeroes(n)结果为109时,n为多少?这个不需要你精确计算出来,你只要找到一个数hi,使得trailingZeroes(hi)比109大,就可以把hi当做正无穷,作为搜索区间的上界。
刚才说了,trailingZeroes函数是单调函数,那我们就可以猜,先算一下trailingZeroes(INT_MAX)的结果,比109小一些,那再用LONG_MAX算一下,远超109了,所以LONG_MAX可以作为搜索的上界。
注意为了避免整型溢出的问题,trailingZeroes函数需要把所有数据类型改成 long:

在这里插入图片描述

代码模拟


 /* 主函数 */
int preimageSizeFZF(int K) {
   // 左边界和右边界之差 + 1 就是答案
   return (int)(right_bound(K) - left_bound(K) )+ 1;
}

/* 搜索 trailingZeroes(n) == K 的左侧边界 */
long left_bound(int target) {
   long lo = 0, hi = Long.MAX_VALUE;
   while (lo < hi) {
       long mid = lo + (hi - lo) / 2;
       if (trailingZeroes(mid) < target) {
           lo = mid + 1;
       } else if (trailingZeroes(mid) > target) {
           hi = mid;
       } else {
           hi = mid;
       }
   }

   return lo;
}

/* 搜索 trailingZeroes(n) == K 的右侧边界 */
long right_bound(int target) {
   long lo = 0, hi = Long.MAX_VALUE;
   while (lo < hi) {
       long mid = lo + (hi - lo) / 2;
       if (trailingZeroes(mid) < target) {
           lo = mid + 1;
       } else if (trailingZeroes(mid) > target) {
           hi = mid;
       } else {
           lo = mid + 1;
       }
   }

   return lo - 1;
}


long trailingZeroes(long n) {
   long res = 0;
   for (long d = n; d / 5 > 0; d = d / 5) {
       res += d / 5;
   }
   return res;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值