Leetcode 793. Preimage Size of Factorial Zeroes Function

Problem:

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.

Example 1:
Input: K = 0
Output: 5
Explanation: 0!, 1!, 2!, 3!, and 4! end with K = 0 zeroes.

Example 2:
Input: K = 5
Output: 0
Explanation: There is no x such that x! ends in K = 5 zeroes.

Note:

  • K will be an integer in the range [0, 10^9].

Solution:

  这道题很有意思,要求我们找出所有数字n的个数,使得n!结尾有K个0。看到这个问题,想到以前的非常规二分搜索方法,在结果可能的范围内查找满足条件的最小值,这里使用了Leetcode 172 Factorial Trailing Zeroes中的函数,然后在getMinimal函数中找到结尾有K个0的最小值,然后getMinimal(K+1)-getMinimal(K)即可。

 1 class Solution {
 2 public:
 3     int trailingZeroes(int n) {
 4         int count_five = 0;  
 5         while(n > 0){  
 6             int k = n/5;  
 7             count_five += k;  
 8             n = k;  
 9         }  
10         return count_five;    
11     }
12     int getMinimal(int K){
13         int start = 0;
14         int end = INT_MAX;
15         while(start < end){
16             int pivot = start+(end-start)/2;
17             if(trailingZeroes(pivot) < K)
18                 start = pivot+1;
19             else
20                 end = pivot;
21         }
22         return start;
23     }
24     int preimageSizeFZF(int K) {
25         return getMinimal(K+1)-getMinimal(K);
26     }
27 };

  然而这个算法却不能通过最后一个测试10^9,原因是结尾有10^9个0的最小值n!,其n已经大于INT_MAX了,当然我们可以修改二分搜索范围,使用int64_t类型即可AC并击败100.00%的提交,代码如下

 1 class Solution {
 2 public:
 3     int64_t trailingZeroes(int64_t n) {
 4         int64_t count_five = 0;  
 5         while(n > 0){  
 6             int64_t k = n/5;  
 7             count_five += k;  
 8             n = k;  
 9         }  
10         return count_five;    
11     }
12     int64_t getMinimal(int64_t K){
13         int64_t start = 0;
14         int64_t end = (int64_t)2*INT_MAX;
15         while(start < end){
16             int64_t pivot = start+(end-start)/2;
17             if(trailingZeroes(pivot) < K)
18                 start = pivot+1;
19             else
20                 end = pivot;
21         }
22         return start;
23     }
24     int64_t preimageSizeFZF(int64_t K) {25         return getMinimal(K+1)-getMinimal(K);
26     }
27 };

  但是这样做未免有投机取巧之嫌,因此我们采用另一种更好的方法,不过这种方法需要一定的观察,我们发现,如果存在n使得n!末尾有K个0,那么答案必然是5(比如K为1,那么5,6,7,8,9均可,当n为10则会引入新的质因数5导致末尾有2个0),如果根本不存在这样的n,那么结果是0,因此答案只有0和5两种可能。现在问题在于如果存在这样的n,n会在什么范围内。n的下限必然为K(观察可知末尾0的数量必然小于等于n),n的上限可以是5*(K+1)(观察可知末尾有K个0的最大值必然小于等于5(K+1),可以用上面的getMinimal验证),因此在这个范围内二分搜索即可,就K=1为例,一旦搜索到5,6,7,8,9中的任意值都可以直接返回,搜索结束还没有找到满足条件的值则返回0.注意即使是这种方法仍然无法避免整形溢出问题,但明显比上面那种无脑二分的方法合理。

  ps:这道题还可以用直接找规律的方法解答,但作为一道算法题,不建议用这种纯观察的方法做。解答在这里

Code:

 

 1 class Solution {
 2 public:
 3     int trailingZeroes(int64_t n) {
 4         int64_t count_five = 0;  
 5         while(n > 0){  
 6             int64_t k = n/5;  
 7             count_five += k;  
 8             n = k;  
 9         }  
10         return count_five;    
11     }
12     int preimageSizeFZF(int K) {
13         int64_t start = K;
14         int64_t end = (int64_t)5*(K+1);
15         while (start < end) {
16             int64_t pivot = start+(end-start)/2;
17             int count = trailingZeroes(pivot);
18             if (count == K) 
19                 return 5;
20             else if (count < K) 
21                 start = pivot + 1;
22             else 
23                 end = pivot;
24         }
25         return 0;
26     }
27 };

 

posted on 2019-02-10 13:45  周浩炜  阅读(271)  评论(0编辑  收藏  举报

导航