【100题】第三十 求从1到n这n个整数的十进制表示中1出现的次数

本文探讨了如何计算从1到n所有整数中数字1出现的总次数。首先介绍了一个简单但效率较低的方法,然后深入讲解了一种利用深度搜索算法进行优化的高级解决方案。

一,题目:输入一个整数n,求从1nn个整数的十进制表示中1出现的次数。

例如输入n=12,从112这些整数中包含1 的数字有11011121一共出现了5次。


二,分析:这是一道广为流传的google面试题。

我们每次判断整数的个位数字是不是1。如果这个数字大于10,除以10之后再判断个位数字是不是1


三,源码

#include <iostream> using namespace std; int NumberOf1(unsigned int n) //返回每一个数字中 1的个数 { int number = 0; while(n) { if(n % 10 == 1) number ++; n = n / 10; } return number; } int total_number(unsigned int n) { int number = 0; for(unsigned int i = 1; i <= n; ++ i) number += NumberOf1(i); return number; } int main() { int count=0; int n=12; cout<<"there have "<<total_number(n)<<" ' 1 ' "<<endl; return 0; }


这个思路有一个非常明显的缺点就是每个数字都要计算1在该数字中出现的次数,因此时间复杂度是O(n)
当输入的n非常大的时候,需要大量的计算,运算效率很低。

下面是一个我看不懂的思路

char num[16];

int len, dp[16][16][2];

int dfs(int pos, int ct, int less)
{
if (pos == len)

return ct;
int &ret = dp[pos][ct][less];
if (ret != -1)

return ret;

ret = 0;
for (int d = 0; d <= (less ? 9 : num[pos] - '0'); d++)
ret += dfs(pos + 1, ct + (d == 1), less || d < num[pos] - '0');

return ret;
}

int NumOf1(int n)
{
sprintf(num, "%d", n);
len = strlen(num);
memset(dp, 0xff, sizeof(dp));
return dfs(0, 0, 0);
}

算法1 暴力枚举每个数字的每一位,统计k出现的次数,时间复杂度为O(n*log_10(n))。 时间复杂度 参考文献 Python 代码 C++ 代码 算法2 统计k在每个数位上出现的次数,并累加得到最终答案。以k=3为例,假设当前处理的是x,x的第i位为di。有以下三种情况: - di>k,则di可以取0~9中任意一个数字,共10^(i-1)种情况,其中i表示第i位,10^(i-1)表示该数位前(i-1)位共有10^(i-1)种情况。 - di<k,则第i位只能取0~di-1中的数字,如果i=1,则0不能出现,共di*(10^(i-1))种情况。 - di=k,则第i位只能取0~di-1中的数字,共(dk-1)*(10^(i-1)) + x%(10^(i-1))+1种情况,其中x%(10^(i-1))表示当前数位前(i-1)位的数字共有x%(10^(i-1))+1种情况,因为x前(i-1)位的数字可以取0~(x%(10^(i-1)))。 注意到以上三种情况中第一种情况第二种情况可以合并成一个公式,故最终的公式为: ans = ∑i=1 ^ len(n)[(n // (10^i))*10^(i-1)+min(max(n%(10^i)−k+1,0),10^(i−1))−(k==0)*10^(i−1)] 其中,n // (10^i)表示取n的前i位数字,max(n%(10^i)−k+1,0)表示取n的第i位数字时,能取到的范围。注意当k=0时,第二项需要减去10^(i-1)。 时间复杂度 参考文献 Python 代码 C++ 代码 算法3 对于每一位数位上出现k的次数进行统计。根据数位的不同分类具体处理,具体如下: - 如果k=0,则枚举当前数位,次数为n/10^(i+1)*10^i,表示高位上数位1~n/10^(i+1)共可以变化10^i种,其中i表示数位从右数第x位,不包含本位(假设最低位为第0位)上的前缀0。 Python 代码 C++ 代码 - 如果当前位数字是k,则统计方式分为三种: - 如果该位数字为0,则次数为前面数位(不包括本身)0~n/10^(i+1)的个数 - 如果该位数字不为0,则次数为前面数位(不包括本身)0~n/10^(i+1)的个数+1 - 如果i=0,即当前位为最低位且为k,则次数1 Python 代码 C++ 代码 - 如果当前位数字小于k,则次数为前面数位(不包括本身)0~n/10^(i+1)的个数 - 如果当前位数字大于k,则次数为前面数位(不包括本身)0~n/10^(i+1)的个数+1 于是有以下代码 时间复杂度 参考文献 Python 代码 C++ 代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值