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 的数量。

示例:

  1. 输入:k = 0
  2. 输出:5
  3. 解释:0!, 1!, 2!, 3!, 4! 均符合 k = 0 的条件。
  1. 输入:k = 5
  2. 输出:0
  3. 解释:没有匹配到这样的 x!,符合 k = 5 的条件。
  1. 输入: k = 3
  2. 输出: 5

解答 & 代码

本题要找能满足 f(x) = k 的非负整数 x 的数量,那么久可以通过二分查找满足 f(x) = k 的非负整数 x左、右边界

  1. 二分查找的下界 left 初始化为 0
  2. 二分查找的上界 right 初始化为 LONG_MAX:因为本题 k 的范围是:[困难] 793. 阶乘函数后 K 个零 - 图1,而 INT_MAX 的阶乘的尾随零个数小于[困难] 793. 阶乘函数后 K 个零 - 图2,因此,需要将 right 初始化为 LONG_MAX
  • 因此,本题的数据类型都改为 long
  1. 二分查找的判定条件,即 mid 的阶乘结果的尾随零个数和目标值 k 的关系
  • mid 的阶乘中尾随零的个数,见:

[中等] 172. 阶乘后的零

  1. class Solution {
  2. private:
  3. // 返回 n 的阶乘结果的尾随零个数
  4. long trailingZeroes(long n)
  5. {
  6. long result = 0;
  7. long div = 5;
  8. while(div <= n)
  9. {
  10. result += n / div;
  11. div *= 5;
  12. }
  13. return result;
  14. }
  15. public:
  16. int preimageSizeFZF(int k) {
  17. // 1. 二分查找 trailingZeroes(n) == k 的左边界
  18. long left = 0;
  19. long right = LONG_MAX;
  20. long leftBoundary = -1;
  21. while(left <= right)
  22. {
  23. long mid = left + (right - left) / 2;
  24. long zeroCnt = trailingZeroes(mid);
  25. if(zeroCnt == k)
  26. {
  27. leftBoundary = mid;
  28. right = mid - 1;
  29. }
  30. else if(zeroCnt > k)
  31. right = mid - 1;
  32. else
  33. left = mid + 1;
  34. }
  35. // 如果不存在左边界,即不存在 trailingZeroes(n) == k,直接返回 0
  36. if(leftBoundary == -1)
  37. return 0;
  38. // 2. 二分查找 trailingZeroes(n) == k 的右边界
  39. left = 0;
  40. right = LONG_MAX;
  41. long rightBoundary = LONG_MAX;
  42. while(left <= right)
  43. {
  44. long mid = left + (right - left) / 2;
  45. long zeroCnt = trailingZeroes(mid);
  46. if(zeroCnt == k)
  47. {
  48. rightBoundary = mid;
  49. left = mid + 1;
  50. }
  51. else if(zeroCnt > k)
  52. right = mid - 1;
  53. else
  54. left = mid + 1;
  55. }
  56. // cout << "k = " << k << ", leftBoundary = " << leftBoundary << ", rightBoundary = " << rightBoundary << endl;
  57. // 3. 返回右边界 - 左边界 + 1
  58. return (int)(rightBoundary - leftBoundary + 1);
  59. }
  60. };

复杂度分析:

  • 时间复杂度 O(logK):二分查找的事件复杂度 O(LONG_MAX) 是一个常数;每次二分调用函数 trailingZeroes(mid) 时间复杂度[困难] 793. 阶乘函数后 K 个零 - 图3~O(logK)
  • 空间复杂度 O(1):

执行结果:

  1. 执行结果:通过
  2. 执行用时:0 ms, 在所有 C++ 提交中击败了 100.00% 的用户
  3. 内存消耗:5.8 MB, 在所有 C++ 提交中击败了 39.03% 的用户