力扣每日一题(793. 阶乘函数后 K 个零)

192 阅读4分钟

题目

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 ,返回 n! 结果中尾随零的数量。

提示 n! = n * (n - 1) * (n - 2) * ... * 3 * 2 * 1

示例1

输入: n = 3
输出: 0
解释: 3! = 6 ,不含尾随 0

示例2

输入: n = 5
输出: 1
解释: 5! = 120 ,有一个尾随 0

示例3

输入: n = 0
输出: 0

这道题目需要用到数学方法来解决,这种题目代码一般不会太长,但是得想的到才行。

阶乘的概念:n! = 1 * 2 * 3 * ······ * n

首先思考一下为什么尾部会出现0呢?

  • 任何一个数当乘以10或者10的倍数时,末尾的0都会不断增加,所以每个阶乘中出现10的倍数,末尾的0就加一。
  • 5乘以任意的偶数,都可以得到10的倍数,并且10也是5的倍数。
  • 任意阶乘数字的偶数的数量一定是大于5的倍数的数字的。
  • 25可以拆分为 5 * 5,每一个5都可以给末尾提供一个0,所以25是可以给末尾提供两个0的。同理50,75也可以提供两个0,125可以提供三个0.
  • 结合以上几点,问题可以转化为n!可以拆分出几个5来,这样问题就简单了。

上代码

function trailingZeroes(n: number): number { 
    let res: number = 0 
    while (n !== 0) {
        n = Math.floor(n / 5) 
        res += n 
    } 
    return res 
};

解释一下这段代码的意思

  • 每次循环都把n除以5。
  • 第一次循环里向下取整得到的数字,是1~n中可以拆分出1个及以上5的数字有几个。
  • 第二次循环得到的数字是可以拆分出2个5的数字有几个。
  • 以此类推,把每次循环得到的值相加就是可拆分出的5的个数。

例如:

100 / 5 = 20,1 ~ 100中可以拆分出1一个及以上的5有20个

20 / 5 = 4,1 ~ 100中拆分出2个及以上5的数有4个:25,50,75,100

4 / 5 向下取整得到0,所以在1 ~ 100中,没有数字可以拆成3个5

最后得到的值即20 + 4 = 24,即100!末尾有24个0

阶乘函数后 K 个零

现在回到这道题,我们已经有办法可以解决n!末尾可以得到几个0,现在要我们解决的是末尾有k个0的数字有几个。

根基上一题提供的例子,我们可以很快得到末尾0个数小于等于23个的n有100个,即0! ~ 99!。

我们把这个数字即为n(23)。

那么有且仅有24个0的个数为n(24) - n(23)。

现在还需要解决这个n(k)函数怎么算。

可以使用二分法来求解n函数,左边界肯定是0,右边界可以得出是小于k * 5的

上最后的代码

function preimageSizeFZF(k: number): number {
    return help(k + 1) - help(k)
};

function help(k): number {
    let max: number = k * 5
    let min: number = 0
    while (max >= min) {
        let mid: number = Math.floor((max + min) / 2)
        if (trailingZeroes(mid) < k) {
            min = mid + 1
        } else {
            max = mid - 1
        }
    }
    return max
}

function trailingZeroes(n: number): number {
    let res: number = 0
    while (n !== 0) {
        n = Math.floor(n / 5)
        res += n
    }
    return res
};

总结

  • 这道题目的难点在于要把数学关系弄清楚,能想到末尾0的个数即是能拆分出5的个数这道题就有思路了。
  • 用数学方式解决问题不太好想到,但是这种代码一般不会太长。

这道题靠自己没做出来,看了好一会的题解才算解决😮‍💨