leetcode:793. 阶乘函数后 K 个零
题目
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 的数量。
示例:
输入:k = 0输出:5解释:0!, 1!, 2!, 3!, 和 4! 均符合 k = 0 的条件。
输入:k = 5输出:0解释:没有匹配到这样的 x!,符合 k = 5 的条件。
输入: k = 3输出: 5
解答 & 代码
本题要找能满足 f(x) = k 的非负整数 x 的数量,那么久可以通过二分查找满足 f(x) = k 的非负整数 x 的左、右边界:
- 二分查找的下界
left初始化为0 - 二分查找的上界
right初始化为LONG_MAX:因为本题k的范围是:,而
INT_MAX的阶乘的尾随零个数小于,因此,需要将
right初始化为LONG_MAX
- 因此,本题的数据类型都改为
long
- 二分查找的判定条件,即
mid的阶乘结果的尾随零个数和目标值k的关系
- 求
mid的阶乘中尾随零的个数,见:
class Solution {private:// 返回 n 的阶乘结果的尾随零个数long trailingZeroes(long n){long result = 0;long div = 5;while(div <= n){result += n / div;div *= 5;}return result;}public:int preimageSizeFZF(int k) {// 1. 二分查找 trailingZeroes(n) == k 的左边界long left = 0;long right = LONG_MAX;long leftBoundary = -1;while(left <= right){long mid = left + (right - left) / 2;long zeroCnt = trailingZeroes(mid);if(zeroCnt == k){leftBoundary = mid;right = mid - 1;}else if(zeroCnt > k)right = mid - 1;elseleft = mid + 1;}// 如果不存在左边界,即不存在 trailingZeroes(n) == k,直接返回 0if(leftBoundary == -1)return 0;// 2. 二分查找 trailingZeroes(n) == k 的右边界left = 0;right = LONG_MAX;long rightBoundary = LONG_MAX;while(left <= right){long mid = left + (right - left) / 2;long zeroCnt = trailingZeroes(mid);if(zeroCnt == k){rightBoundary = mid;left = mid + 1;}else if(zeroCnt > k)right = mid - 1;elseleft = mid + 1;}// cout << "k = " << k << ", leftBoundary = " << leftBoundary << ", rightBoundary = " << rightBoundary << endl;// 3. 返回右边界 - 左边界 + 1return (int)(rightBoundary - leftBoundary + 1);}};
复杂度分析:
- 时间复杂度 O(logK):二分查找的事件复杂度 O(LONG_MAX) 是一个常数;每次二分调用函数
trailingZeroes(mid)时间复杂度~O(logK)
- 空间复杂度 O(1):
执行结果:
执行结果:通过执行用时:0 ms, 在所有 C++ 提交中击败了 100.00% 的用户内存消耗:5.8 MB, 在所有 C++ 提交中击败了 39.03% 的用户
