算法练习week3--leecode792

题目描述

这道题的题目名称非常的难懂,但是读了题目内容以后,就不难理解了,定义函数f(x)为x!的末尾0的个数,现在给了我们一个非负整数K,问使得f(x)=K成立的非负整数的个数。

问题分析

之前做过一道有关阶乘末尾零的个数的题,从那道里知道了末尾0其实是由2和5相乘为10得到的,而阶乘中2的数量远多于5的个数,所以10的个数就只取决于5的个数。

需要注意的一点就是,像25,125,这样的不只含有一个5的数字需要考虑进去。比如,24的阶乘末尾有4个0,分别是5,10,15,20中的四个5组成的,而25的阶乘末尾就有6个0,分别是5,10,15,20中的各一个5,还有25中的两个5,所以共有六个5,那么就不存在其阶乘数末尾有5个0的数。

除此之外,我们知道20,21,22,23,24,这五个数的阶乘数末尾零的个数其实是相同的,都是有4个,因为它们包含的5的个数相同。而19,18,17,16,15,这五个数末尾零个数相同,均为3。那么我们其实可以发现,每五个数,必会至少多出1个5,有可能更多。所以阶乘末尾零个数均为K个的x值,只有两种情况,要么是5,要么是0。

算法设计

算法1

基于之前那道题的解法,我们有了如何快速求一个给定的数字阶乘末尾零的个数,那么我们只要找到了一个这样的数,其阶乘末尾零的个数等于K的话,那么就说明总共有5个这样的数,返回5,反之,如果找不到这样的数字,就返回0。那么像这种选一个candidate数字,再用二分搜索进行验证的操作。首先要确定二分搜索法的范围,左边界为0,关键是来确定右边界。

我们来分析一下,一个数字的阶乘末尾零个数为K,那么这个数字能有多大,就拿前面举的例子来说吧,末尾有4个0的最大数字是24,有六个0的最大是29,那么我们发现它们都不会超过5*(K+1)这个范围,所以这就是我们的右边界。然后进行二分搜索法。

注意:右边界可能会超过整型数范围,所以要用长整型来表示。

class Solution {
public:
    int preimageSizeFZF(int K) {
        long left = 0, right = 5L * (K + 1);
        while (left < right) {
            long mid = left + (right - left) / 2;
            long cnt = numOfTrailingZeros(mid);
            if (cnt == K) return 5;
            else if (cnt < K) left = mid + 1;
            else right = mid;
        }
        return 0;
    }
    long numOfTrailingZeros(long x) {
        long res = 0;
        for (; x > 0; x /= 5) {
            res += x / 5;
        }
        return res;
    }
};

算法2

在算法1 的基础上进行代码模块整合,将while循环柔和进去即可,代码减少了30%。

class Solution {
public:
    int preimageSizeFZF(int K) {
        long left = 0, right = 5L * (K + 1);
        while (left < right) {
            long mid = left + (right - left) / 2, cnt = 0;
            for (long i = 5; mid / i > 0; i *= 5) {
                cnt += mid / i;
            }
            if (cnt == K) return 5;
            else if (cnt < K) left = mid + 1;
            else right = mid;
        }
        return 0;
    }
};

算法3

通过后续的资料查询,我发现有博主通过规律寻找的方法,找出了其中蕴含的规律,顺利解决该问题。不过我以为,这样的解法不具有算法设计的巧妙性,有一点运气的程序在里面。所以,我在这里不做赘述,截图供参考,丰富思维,同时,我也按照他的思路实现了他的算法。

扫描二维码关注公众号,回复: 3888398 查看本文章
class Solution {
public:
    int preimageSizeFZF(int K) {
        if (K < 5) return 5;
        int base = 1;
        while (base * 5 + 1 <= K) {
            base = base * 5 + 1;
        }
        if (K / base == 5) return 0;
        return preimageSizeFZF(K % base);
    }
};

猜你喜欢

转载自blog.csdn.net/pygmelion/article/details/82780887