100题_25 在从1到n的正数中1出现的
输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数。例如输入12,从1到12这些整数中包含1 的数字有1,10,11和12,1一共出现了5次。
如果按照常规的思路来解决这个问题:首先考虑如何计算一个数n中1的个数,我们可以采用类似于求二进制1的个数的求法,每次除,并取余,看看余是不是1来确定。这样总共的时间复杂度将是O(n),这样是有些慢的。
考虑,如果知道了n的1的个数,在求n+1中的1的个数,是不是可能不需要完全去计算了?因此,我们可以看到上面说到的常规的方法肯定做了一定的重复计算。我们可以断定一定存在更加快速的算法。这就需要我们来观察了。
考虑一个比较大一点数,如21345。很直观的,我们可以将1~21345分成两部分①1~1345②1346~21345。对于第二部分,首先考虑万位,万位为1,其他位任意,有个。然后考虑其他位,有
,其中的第一个2表示,21345的最高位,5表示21345的数位。对于第一部分,我们可以采用递归求解。
另外,我们还要考虑一个特殊情况,那就是,如果首位为1,比如11345,那怎么办呢?我们发现考虑最高位为1的情况应该有所变化,应该为1345+1(除去最高位加1)。
这样,假设对于一个数n,最高位为f,位数为l(例如,n=21345,f=2,l=5)。那么1~n中,1的数量g(n)为:
有了公式就好办了,下面是简单实现的代码:
#include <iostream>
using namespace std;
int pow(int a, int b)
{
if (b == 0)
return 1;
if (b < 0)
return 0;
int r = 1;
for (int i = 0; i < b; i++)
r *= a;
return r;
}
// 求取n的位数和最高位 void countNumber(int n, int &digit, int &high)
{
digit = 1;
while (n >= 10)
{
n /= 10;
digit ++;
}
high = n;
}
int numberOfOneBetweenOneAndN(const int &n)
{
if (n == 0)
return 0;
if (n < 10)
return 1;
int digit, high;
// 求取n的位数和最高位 countNumber(n, digit, high); int pd = pow(10, digit-1);
int dh = n - pd * high;
int numFirst;
if (high == 1)
numFirst = dh + 1;
else numFirst = pd; return numFirst + high * (digit - 1) * pd / 10 + numberOfOneBetweenOneAndN(dh);
}
int main()
{
cout<<numberOfOneBetweenOneAndN(21345)<<endl;
return 0;
}

本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名小橋流水(包含链接)。如您有任何疑问或者授权方面的协商,请给我发邮件。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· [AI/GPT/综述] AI Agent的设计模式综述