leetcode 793. Preimage Size of Factorial Zeroes Function
题目描述
Let f(x)
be the number of zeroes at the end of x!
. (Recall that x! = 1 * 2 * 3 * ... * x
, and by convention, 0! = 1
.)
For example, f(3) = 0
because 3! = 6
has no zeroes at the end, while f(11) = 2
because 11! = 39916800
has 2 zeroes at the end. Given K, find how many non-negative integers x have the property that f(x) = K
.
Note:
K
will be an integer in the range[0, 10^9]
.
Difficulty: hard
793. Preimage Size of Factorial Zeroes Function
中文描述
题目定义了一个函数
f(x)
f
(
x
)
,表示
x!
x
!
后末尾有几个0。比如
f(3)=0,f(11)=2
f
(
3
)
=
0
,
f
(
11
)
=
2
,因为
3!=6,11!=39916800
3
!
=
6
,
11
!
=
39916800
,所以
3!
3
!
后面没有零,所以
f(3)=0
f
(
3
)
=
0
,而
11!
11
!
最后有2个零,所以
f(11)=2
f
(
11
)
=
2
。现在给定一个
K
K
,问有几个,能使得
f(x)=K
f
(
x
)
=
K
。
输入格式
输入一个值K
,K
表示结尾有K
个零。
Examples:
Input: K = 0
Output: 5
解释:
0!=1, 1!=1, 2!=2, 3!=6, 4!=5 结尾都没有零,所以有5个数满足 f(x)=0 f ( x ) = 0 .Input: K = 5
Output: 0
解释:
没有 x x 能够满足,所以结果是0
解答思路
记得leetcode上有一题是给定一个 x x ,判断末尾有几个零。172. Factorial Trailing Zeroes。这题则是它的变形。
二分查找
1.通常情况下我们考虑遍历 x x 来寻找答案。而随着的增大, f(x) f ( x ) 也是一个增加的态势。所以自然而然的我们会考虑到使用二分查找 x x 来优化该问题。
2.我们首先取定的最大最小的范围,最小范围很好确定,从0开始即可,而最大范围则需要考虑下。通过观测得到 0!,1!,2!,3!,4! 0 ! , 1 ! , 2 ! , 3 ! , 4 ! 末尾没有零, 5!,6!,7!,8!,9! 5 ! , 6 ! , 7 ! , 8 ! , 9 ! 末尾有1个零, 10!,11!,12!,13!,14! 10 ! , 11 ! , 12 ! , 13 ! , 14 ! 末尾有2个零, 15!,16!,17!,18!,19! 15 ! , 16 ! , 17 ! , 18 ! , 19 ! 末尾有3个零, 20!,21!,22!,23!,24! 20 ! , 21 ! , 22 ! , 23 ! , 24 ! 末尾有4个零,到了 25!,26!,27!,28!,29! 25 ! , 26 ! , 27 ! , 28 ! , 29 ! 末尾有6个零。我们发现每过5个数,末尾的零至少增加1个,并且这样连续的5个数的 f(x) f ( x ) 是一样的(因为能够产生零,一定是因数中包含2*5,或则直接包含10的情况。因数中包含10只有10,20这样的数,而包含5则是只有5,15这样的数,所以每连续5个数的结果都是一样的)。所以对于 K K ,我们只需取到 5K 5 K 的位置即可。
3.对 x x 进行二分查找。如果存在,则根据第二步的分析,我们知道至少有5个数能满足 f(x)=K f ( x ) = K ,所以返回5。否则就不存在,返回0。
4.复杂度估计 O(logK) O ( l o g K )
代码
class Solution(object):
def preimageSizeFZF(self, K):
"""
:type K: int
:rtype: int
35MS
"""
if K == 0:
return 5
# 计算x!的末尾有几个0
def trailingZeroes(n):
if n == 0:
return 0
import math
k = int(math.log(n, 5))
ans = 0
for i in range(1, k + 1):
ans += n // 5 ** i
return ans
# 选择合适的最大位置,通过观测可以发现K*5必然比x大(f(x) = K)
hight, low = K * 5, 5
# 之后就是二分查找,如果有一定为5个连续的,否则就为0
while low <=hight:
mid = (low + hight) // 2
a = trailingZeroes(mid)
if a > K:
hight = mid - 1
elif a < K:
low = mid + 1
else:
return 5