【Leetcode】172. 阶乘后的零 & 793. 阶乘函数后 K 个零

博客探讨了阶乘中零的个数问题,首先解释了0出现在阶乘末尾的原因,即2和5的因子。通过分析,得出求阶乘末尾零个数可以转化为找5的因子个数。对于793题,利用二分查找法解决寻找阶乘后K个零的前驱和后继值。文章提供了具体的代码实现,并讨论了边界条件,如Integer.MAX_VALUE和Long.MAX_VALUE的适用性。
摘要由CSDN通过智能技术生成

172. 阶乘后的零

在这里插入图片描述
首先,两个数相乘结果末尾有 0,一定是因为两个数中有因子 2 和 5,因为 10 = 2 x 5。
所以,问题可以转化成阶乘中总共有多少因子2和5。进一步,我们发现,因子2总是比5个数多,因为每个偶数都有因子2,而只有5的倍数才包含因子5,所以问题可以进一步转化为找因子5的个数
25!为例,5,10,15,20,25都是5的倍数,但是需要注意25中包含了两个5,所以总共有1+1+1+1+2=6个因子5。
进一步延伸,以125!为例,首先找到5,10,...,120,125总共125/5=25个因子5;而进一步的,我们还可以找到25,50,...,100,125共125/25=5个因子5,还可以找到125/125=1个因子5,所以总共有125!中总共有25+5+1=31个因子5,也就是说阶乘末尾有31个0。
下面是代码:

class Solution {
    public int trailingZeroes(int n) {
        int res = 0;
        int f = 5;//5的倍数factor
        while(n >= f) {
           res += n / f;
           f *= 5;
        }
        return res;
    }
}

这样,这道题就解决了,时间复杂度是底数为 5 的对数,也就是 O(logN),我们看看下如何基于这道题的解法完成下一道题目。

793. 阶乘函数后 K 个零

在这里插入图片描述
首先我们可以知道阶乘后0的个数随n!增大而增加,而n!随n的增大而增加,针对这种单调函数找区间的问题,显然我们可以使用二分查找的方法。
那么二分查找的区间下界可以取0,问题的关键在于如何确定区间上界,理论上应该是取正无穷,但是数学上的正无穷不可能在代码中表示出来。所以,进一步观察题目和测试数据,K的取值范围为0 <= k <= 10^9,那么我们只要找到一个数x,是的x! 后0的个数大于k即可,我们刚好可以通过172题的方法去找这个数。
首先我们可以测试一下Integer.MAX_VALUE = 2^31 - 1,发现INT_MAX后总共有536,870,902 < 10^9,所以需要尝试更大的数比如Long.MAX_VALUE = 2^63 - 1,发现满足条件。下一步我们使用二分查找方法即可。

class Solution {
    long trailingZeroes(long n) {
        long res = 0;
        for (long d = n; d / 5 > 0; d = d / 5) {
            res += d / 5;
        }
        //System.out.println(res);
        return res;
    }

    public int preimageSizeFZF(int k) {
        return (int)(right_bound(k) - left_bound(k) + 1);
    }

    long left_bound(int target){
        long lo = 0, hi = Long.MAX_VALUE;
        while(lo <= hi) {
            long mid = lo + (hi - lo) / 2;
            long tra = trailingZeroes(mid);
            if(tra > target) {
                hi = mid - 1;
            }else if(tra == target) {
                hi = mid - 1;
            }else if(tra < target) {
                lo = mid + 1;
            }
        }
        return hi + 1;
    }
    
    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 - 1;
            } else {
                lo = mid + 1;
            }
        }
    return lo - 1;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值