📅  最后修改于: 2023-12-03 15:07:51.648000             🧑  作者: Mango
题目描述:
给定一个数字 N,求 N!(N 的阶乘)中某一位数字出现的次数。
例如: N = 11, 某一位数字为 1,则该数字在 N! 中出现的次数为 4。
具体解释如下:
N! = 11! = 39916800,其中数字 1 在个位、十位和百位上均各出现一次,所以该数字在 N! 中出现的次数为 4。
解题思路:
我们可以将该题目分成两个子问题:
1.求N!的值
2.求数字在N!中出现的次数
对于第一个子问题,可以使用递归的思想进行求解,即
N!=N*(N-1)!
其递归终止条件为 N = 1 或 N = 0,此时 N! 的值为 1。
对于第二个子问题,我们可以将 N! 中所有的数字拆分成个位、十位、百位等等的位置,然后再统计数字在各位出现的次数即可。
具体实现如下:
int countDigitOne(int n) {
int res = 0;
for (long long i = 1; i <= n; i *= 10) { // i表示位数上的值,个位为1,十位为10,百位为100...
int divider = i * 10; // divider表示下一个位数的值
res += (n / divider) * i + min(max(n % divider - i + 1, 0LL), i); // 核心代码,分三部分进行解析
}
return res;
}
代码解析:
1.首先我们定义变量 i,表示当前处理的位数值。从个位开始,依次处理到最高位。
2.然后我们定义变量 divider,表示下一个处理的位数值。
3.接下来就是核心代码部分,分为三部分进行解析:
3.1. (n / divider) * i:表示当前位数上数字在所有可能的数字当中一共出现的次数,例如对于个位数字 1 来说,其出现次数为 (n / 10) * 1。
3.2. n % divider - i + 1:表示当前位数上可能的第一个数字,例如对于个位数字 1 来说,其可能的第一个数字为 1,即 n % 10 - 1 + 1 = n % 10。
3.3. min(max(n % divider - i + 1, 0LL), i):表示当前位数上数字在可能的数字中实际出现的次数,我们需要将其限制在 [1, i] 的范围内,因为数字最多只能出现 i 次。
4.最后返回 res 即可。
代码时间复杂度:O(logn),空间复杂度:O(1)。
参考资料:
[1] 力扣(LeetCode)原题链接