1. Job Roadmap
  2. Home
  3. Problem Set
  4. codenotelist
  5. Forum
  6. course
  7. Shore Share Sessions
  8. Record
  1. Login
  2. Sign Up
  3. Language
    1. English
    2. 한국어
    3. 简体中文
    4. 正體中文
    ZhContent TextSol AI分析

题解链接

P4777.阶乘末尾0(非hot100)

    1000ms Tried: 130 Accepted: 55 Difficulty: 3 所属公司 : Hot100
    算法与标签>数学

在线刷题

本题来源于26年3月底华为实习AI方向手撕真题,同学写的面经在这

原题来自LeetCode 172. 阶乘后的零

题目内容

给定一个数nnn,nnn 的阶乘末尾有多少数字 ′0′'0'′0′ ?

输入描述

一个正整数 nnn 。

1≤n≤1051≤n≤ 10^51≤n≤105

输出描述

nnn 的阶乘包含 ’0′’0'’0′ 的数量。

**样例1 **

输入

7

输出

1

说明

777 的阶乘是 504050405040 ,其中有 111 个数字 ′0′'0'′0′

思路

1.直接算阶乘,不现实:因为nnn 很大,数据量大了之后就超出计算机能够存储的整数范围了。

2.大数模拟呢?也不现实:

因为每次n∗(n+1)n * (n + 1)n∗(n+1) ,数位几乎是成线性增长的。当n=105n = 10^5n=105 时,n!n!n!的数位长度是4∗1054 *10^54∗105 。

考虑每一次乘法的复杂度是Ω(n)\Omega(n)Ω(n)的,一共要做nnn次乘法计算,复杂度是Ω(n2)\Omega(n^2)Ω(n2)的。101010^{10}1010次运算,计算机大概要跑200200200秒左右。显然不符合要求。

3.正确解法:想清楚阶乘中末尾的000是怎么来的

一个数末尾为什么会有000 ? 因为它的因子里有101010 。

而10=2×510 = 2 \times 510=2×5

所以问题本质上就变成了:

在 n!n!n! 的质因子分解里,一共能凑出多少对 2×52 \times 52×5。

**例如7的阶乘后面为什么有1个0?因为它质因分解后能凑出一对2×52 \times 52×5**👇

根据下图结果不难发现,阶乘里的因子222 的个数永远比 因子555 多👇

[1,n][1,n][1,n] 中含有因子222的数,也就是偶数,有n2\frac{n}{2}2n​ 个

[1,n][1,n][1,n] 中含有因子555的数,也就是555的倍数的数,有n5\frac{n}{5}5n​ 个。 明显是少于偶数的个数的。

所以问题变成了:n!n!n!里有多少个因子555 。

从数轴上来看,就是统计方块5的个数👇

那么我们可以循环[1,n][1,n][1,n]中的每个数,不停的从这个数里除掉555 , 直到没有因子555为止

def count_fives_in_factorial (n):
    ans_5 = 0
    # 枚举每一个数
    for i in range(1 , n + 1):
        # 统计这个数里有多少个因子5
        x = i 
        while x % 5 == 0:
            x //= 5
            ans_5 += 1
    return ans_5


n = int(input())
print(count_fives_in_factorial(n))
#include <iostream>
using namespace std;

int count_fives_in_factorial(int n) {
    int ans_5 = 0;
    // 枚举每一个数
    for (int i = 1; i <= n; i++) {
        // 统计这个数里有多少个因子5
        int x = i;
        while (x % 5 == 0) {
            x /= 5;
            ans_5 += 1;
        }
    }
    return ans_5;
}

int main() {
    int n;
    cin >> n;
    cout << count_fives_in_factorial(n) << endl;
    return 0;
}
import java.util.Scanner;
public class Main {
    public static int count_fives_in_factorial(int n) {
        int ans_5 = 0;
        // 枚举每一个数
        for (int i = 1; i <= n; i++) {
            // 统计这个数里有多少个因子5
            int x = i;
            while (x % 5 == 0) {
                x /= 5;
                ans_5 += 1;
            }
        }
        return ans_5;
    }
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        System.out.println(count_fives_in_factorial(n));
    }
}

优化:逐行计算

相对于[1,n][1,n][1,n] , 含有因子555的数是稀疏的,也就是上述循环中很多<不是555的倍数的数>是完全没必要去统计的👇

所以比起统计所有正整数,更直接的办法是:

有多少个数能提供 1 个 5

有多少个数能提供 2 个 5

有多少个数能提供 3 个 5

...

而一个个去找含有因子555的数复杂度过高,我们考虑逐行计算!

先考虑第一行的5的个数,它本质上就是在计算:有多少个数至少包含一个因子555 , 这个个数显然是n5\frac{n}{5}5n​

再考虑第二行的5的个数,它本质上就是在计算:有多少个数至少包含两个555 , 这个个数显然是n52=n25\frac{n}{5^2}=\frac{n}{25}52n​=25n​

所以逐行计算我们的答案就是:n5+n25+n125+...\frac{n}{5} + \frac{n}{25} + \frac{n}{125} + ...5n​+25n​+125n​+... , 一直到分式为零为止

代码实现如下👇

from typing import List

class Solution:
    def trailingZeroes(self, n: int) -> int:
        # 统计阶乘末尾 0 的个数,本质是统计因子 5 的个数
        ans_5 = 0
        i = 5
        # 依次统计 5、25、125... 的倍数个数
        while i <= n:
            ans_5 += n // i 
            i *= 5
        return ans_5

n = int(input())
print(Solution().trailingZeroes(n))
#include <iostream>
using namespace std;

class Solution {
public:
    int trailingZeroes(int n) {
        // 统计阶乘末尾 0 的个数,本质是统计因子 5 的个数
        int ans_5 = 0;
        int i = 5;
        // 依次统计 5、25、125... 的倍数个数
        while (i <= n) {
            ans_5 += n / i;
            i *= 5;
        }
        return ans_5;
    }
};

int main() {
    int n;
    cin >> n;
    cout << Solution().trailingZeroes(n) << endl;
    return 0;
}

import java.util.Scanner;

class Solution {
    public int trailingZeroes(int n) {
        // 统计阶乘末尾 0 的个数,本质是统计因子 5 的个数
        int ans_5 = 0;
        int i = 5;
        // 依次统计 5、25、125... 的倍数个数
        while (i <= n) {
            ans_5 += n / i;
            i *= 5;
        }
        return ans_5;
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        System.out.println(new Solution().trailingZeroes(n));
    }
}

面试问答

1.本题的算法思路是什么?(朴素做法)

一道经典的数学题。阶乘末尾零的个数取决有多少个因子101010 , 而因子101010由质因子对2,52,52,5 所决定。又由于555的因子个数一定少于222的。所以考虑直接计算阶乘中因子555的个数。循环枚举[1,n][1,n][1,n] , 每次不断用555除尽这个数, 从而得到每个数中因子555的个数,最后输出答案即可。

时间复杂度:外层循环nnn次,内层循环并不是每次都会触发,它本质在计算[1,n][1,n][1,n]范围内因子555的总个数,总共计算n5+n25+n125+...<log5n\frac{n}{5} + \frac{n}{25}+\frac{n}{125}+... < log_5 n5n​+25n​+125n​+...<log5​n 次。总的循环次数不超过log5nlog_5 nlog5​n , 则复杂度为O(log5n)O(log_5 n)O(log5​n)

一般面试中大概率不要用最优解法,可以先实现朴素解法。如果面试官要求进一步优化,可以再考虑写最优解。

登录后即可使用 AI 分析。

模式
倒计时时长
:

最长 10 小时 59 分;应用后按此时长重新开始。

提示:点击提交记录在左侧题面区域查看详情
题库
AI分析设置
留空使用官方API Key,每天有次数限制(自定义API Key仅限会员和管理员使用,不限次数)
会员和管理员可切换模型;切到 Kimi/智谱/通义/豆包时需填写对应供应商 API Key
升级会员,可将运行与提交冷却时间缩短至 1 秒起

Status

  • Judging Queue
  • Service Status

Development

  • Open Source

Support

  • Help
  • Contact Us

About

  • About
  • Privacy
  • Terms of Service
  • Copyright Complaint
  1. Language
    1. English
    2. 한국어
    3. 简体中文
    4. 正體中文
  2. Legacy mode
  3. Theme
    1. Light
    2. Dark
  1. 京ICP备2025123107号-1
  2. Worker 1, 70ms
  3. Powered by Hydro v5.0.0-beta.18 Community
CLOSE


ScanQRCodePrompt

请使用微信扫描下方二维码完成注册

Forgot password or username?