编程之美计算0到N中包含数字1的个数
转自:http://blog.csdn.net/hongjuntu123/article/details/8743266
有这样一个函数f(n),对于任意正整数n,它表示从 0 到 n 之间出现“1”的个数,比如 f(1) = 1, f(13) = 6,请列出从 1 到 1234567890 中所有的 f(n) = n 的n, 要求准确快速.
相信很多人都能立刻得出以下的解法:
for(n:N)
{
判断n包含1的个数;
累加计数器;
}
这是最直接的解法,但遗憾的是,时间复杂程度为O(N*logN)。因为还需要循环判断当前的n的各位数,该判断的时间复杂程度为O(logN)。
接下来就应该思考效率更高的解法了。说实话,这道题让我想起另外一道简单的算法题:
N为正整数,计算从1到N的整数和。
很多人都采用了循环求解。然后利用初等数学知识就知道S=N*(N+1)/2,所以用O(1)的时间就可以处理。
再回到本道题目,同理应该去寻找到结果R与N之间的映射关系。
分析如下:
假设N表示为a[n]a[n-1]...a[1],其中a[i](1<=i<=n)表示N的各位数上的数字。
c[i]表示从整数1到整数a[i]...a[1]中包含数字1的个数。
x[i]表示从整数1到10^i - 1中包含数字1的个数,例如,x[1]表示从1到9的个数,结果为1;x[2]表示从1到99的个数,结果为20;
当a[1]=0时,c[1] = 0;
当a[1]=1时,c[1] = 1;
当a[1]>1时,c[1] = 1;
当a[2]=1时,c[2] = a[1] +1+ c[1] + x[1];
当a[2]>1时,c[2] = a[2]*x[1]+c[1]+10;
当a[3]=1时,c[3] = a[2]*a[1] +1+ c[2] + x[2];
当a[3]>1时,c[3] = a[3]*x[2]+c[2]+10^2;
......
以此类推
当a[i]=1时,c[i] = a[i-1]*...*a[1] +1+ c[i-1]+x[i-1];
当a[i]>1时,c[i] = a[i]x[i-1]+c[i-1]+10^(i-1);
实现的代码如下:
- public static int search(int _n)
- {
- int N = _n/10;
- int a1 = _n%10,a2;
- int x = 1;
- int ten = 10;
- int c = a1 == 0?0:1;
- while(N > 0)
- {
- a2 = N%10;
- if(a2 == 0);
- else if(a2 == 1)c = a1 + 1 + x + c;
- else c = a2*x + c + ten;
- a1 = 10*a1 + a2;
- N /=10;
- x = 10*x + ten;
- ten *= 10;
- }
- return c;
- }
而以上解法的时间复杂程度只有O(logN)
/
编程之美之1的数目
1 的数目
给定一个十进制正整数 N,写下从 1 开始,到 N 的所有整数,
然后数一下其中出现的所有“1”的个数。
例如:
N= 2,写下 1,2。这样只出现了 1 个“1”。
N= 12,我们会写下 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12。这样,1的个数是 5。
问题是:
写一个函数f(N) 返回1到N之间出现的“1”的个数,比如f(12)=5。
这个问题看上去并不是一个困难的问题,因为不需要太多的思考,大家都能找到一个最简单的方法来计算 f(N),那就
是从1开始遍历到N,将其中每一个数中含有“1”的个数加起来,
自然就得到了从1到N所有“1”的个数的和.C语言实现如下:
int Count1(int n)
{
int iNum=0;
while(n!=0)
{
iNum+=(n%10==1)?1:0;
n/=10;
}
return iNum;
}
int Count2(int n)
{
int iCount=0;
for(int i=0;i<=n;i++)
{
iCount+=Count1(i);
}
return iCount;
}
int main()
{
int x;
scanf("%d",&x);
printf("%d",Count2(x));
return 0;
}
但是这个算法的致命问题是效率,它的时间复杂度是O(N)×计算一个整数数字里面“1”的个数的复杂度 = O(N *log2 N),如果给定的 N 比较大,则需要很长的运算时间才能得到计算结果。我试着输入100000000,一共用了大概12秒,貌似有点长,不够比作者说的40S快多了,作者的电脑有点旧…….- .-
解法二
<编程之美>先从一些简单的情况开始观察:
如果N是一位数,可以确定f(N)=1
如过是二位数,如果 N=13,那么从 1 到 13 的所有数字:1、2、3、4、5、6、
7、8、9、10、11、12、13,个位和十位的数字上都可能有 1,我们可以将它们分开来考虑,个位出现 1 的次数有两次:1 和 11,十位出现 1 的次数有 4 次:10、11、12 和 13,所以 f(N)=2+4=6。要注意的是 11 这个数字在十位和个位都出现了 1, 但是 11 恰好在个位为 1 和十位为 1 中被计算了两次,所以不用特殊处理,是对的。再考虑 N=23 的情况,它和 N=13 有点不同,十位出现 1 的次数为 10 次,从 10 到 19,个位出现 1 的次数为 1、11 和 21,所以f(N)=3+10=13。通过对两位数进行分析,我们发现,个位数出现 1 的次数不仅和个位数字有关,还和十位数有关:如果 N 的个位数大于等于 1,则个位出现 1 的次数为十位数的数字加 1;如果N 的个位数为 0,则个位出现 1 的次数等于十位数的数字。而十位数上出现 1 的次数不仅和十位数有关,还和个位数有关:如果十位数字等于 1,则十位数上出现 1 的次数为个位数的数字加 1;如
果十位数大于 1,则十位数上出现 1 的次数为 10。
f(13) = 个位出现1的个数 + 十位出现1的个数 = 2 + 4 = 6;
f(23) = 个位出现1的个数 + 十位出现1的个数 = 3 + 10 = 13;
f(33) = 个位出现1的个数 + 十位出现1的个数 = 4 + 10 = 14;
…
f(93) = 个位出现1的个数 + 十位出现1的个数 = 10 + 10 =
20;
接着分析 3 位数,
如果 N = 123:
个位出现 1 的个数为 13:1, 11, 21, …, 91, 101, 111, 121
十位出现 1 的个数为 20:10~19, 110~119
百位出现 1 的个数为 24:100~123
f(23)= 个位出现 1 的个数 + 十位出现 1 的个数 + 百位出现 1 的次数 = 13 + 20 + 24 = 57;同理我们可以再分析 4 位数、 位数。 根据上面的一些尝试,下面我们推导出一般情况下,从 N 得
到 f(N)的计算方法: 假设 N=abcde,这里 a、b、c、d、e 分别是十进制数 N 的各个数位上的数字。如果要计算百位上出现 1 的次数,它将会受到三个因素的影响:百位上的数字,百位以下(低位)的数字,百
位(更高位)以上的数字。如果百位上的数字为 0,则可以知道,百位上可能出现 1 的次
数由更高位决定,比如 12 013,则可以知道百位出现 1 的情况可能
是 100~199,1 100~1 199,2 100~2 199,…,11 100~11 199,
一共有 1 200 个。也就是由更高位数字(12)决定,并且等于更高
位数字(12)×当前位数(100)。
如果百位上的数字为 1,则可以知道,百位上可能出现 1 的次数不仅受更高位影响,还受低位影响,也就是由更高位和低位共同决定。例如对于 12 113,受更高位影响,百位出现 1 的情况是 100~199,1 100~1 199,2 100~2 199,…,11 100~11 199,一共 1 200个,和上面第一种情况一样,等于更高位数字(12)×当前位数(100)。但是它还受低位影响,百位出现 1 的情况是 12 100~12 113,一共114 个,等于低位数字(123)+1。 如果百位上数字大于 1(即为 2~9),则百位上可能出现 1的次数也仅由更高位决定,比如 12 213,则百位出现 1 的可能性为:100~199,1 100~1 199,2 100~2 199,…,11 100~11 199,12 100~12 199,一共有 1 300 个,并且等于更高位数字+1(12+1)
×当前位数(100)。通过上面的归纳和总结,我们可以写出如下的更高效算法来
计算 f(N):
int Sumls(int n)
{
int iCount=0,iFactor=1,iLowerNum=0,iCurrNum=0,iHigherNum=0;
while(n/iFactor!=0)
{
iLowerNum=n-(n/iFactor)*iFactor;
iCurrNum=(n/iFactor)%10;
iHigherNum=n/(iFactor*10);
switch(iCurrNum)
{
case 0:
iCount+=iHigherNum*iFactor;
break;
case 1:
iCount+=iHigherNum*iFactor+iLowerNum+1;
break;
default:
iCount+=(iHigherNum+1)*iFactor;
break;
}
iFactor*=10;
}
return iCount;
}
int main()
{
int x;
scanf("%d",&x);
printf("%d",Sumls(x));
return 0;
}
我试着输入100000000瞬间就显示出结果了,编程之美说效率至少提高了40000倍…
转载于:https://www.cnblogs.com/vipwtl/p/5366877.html
编程之美计算0到N中包含数字1的个数相关推荐
- mysql dp.cal 显示汉子_计算1到N中各个数字出现的次数 --数位DP
题意:给定一个数n,问从1到n中,0~9这10个数字分别出现了多少次.比如366这个数,3出现了1次,6出现了2次. 题解:<剑指offer>P174:<编程之美>P132 都 ...
- 在1至2019中,有多少个数的数位中包含数字9?
在1至2019中,有多少个数的数位中包含数字9?注意,有的数中的数位中包含多个9,这个数只算一次.例如,1999这个数包含数字9,在计算只是算一个数. 答:在1至2019中,有544个数的数位中包含数 ...
- R语言自定义函数计算dataframe每列中的缺失值NA的个数、缺失值问题及其填充示例
R语言自定义函数计算dataframe每列中的缺失值NA的个数.缺失值问题及其填充示例 目录
- 1至2019中,有多少个数的数位中包含数字9
问题描述 在1至2019中,有多少个数的数位中包含数字9? 注意,有的数中的数位中包含多个9,这个数只算一次.例如,1999这个数包含数字9,在计算只是算一个数. 答案提交 这是一道结果填空的题,你只 ...
- C++:求整数num中包含因子k的个数
编写函数factors(num,k),函数功能是:求整数num中包含因子k的个数,如果没有该因子则返回0,例如:32=22222,则factors(32,2)=5.要求输入输出均在主函数中完成. #i ...
- Java统计数组中各个数字出现的个数和字符串中各个字符出现的个数
一.前言 对于统计个数问题,我们一般利用HashMap来解决,其中key表示原始元素值,value表示其出现个数或出现次数.主要步骤为: 1.创建一个HashMap<Character, Int ...
- 【SQL开发实战技巧】系列(十二):三问(如何对字符串字母去重后按字母顺序排列字符串?如何识别哪些字符串中包含数字?如何将分隔数据转换为多值IN列表?)
系列文章目录 [SQL开发实战技巧]系列(一):关于SQL不得不说的那些事 [SQL开发实战技巧]系列(二):简单单表查询 [SQL开发实战技巧]系列(三):SQL排序的那些事 [SQL开发实战技巧] ...
- Java实验9 T4.统计文件的所有短文中包含英文字母的个数
题目要求 在文本文件bigbook.txt中包含有很长篇幅的英语短文,编写程序要求统计文件的所有短文中包含英文字母"A"的个数,并显示统计的时间. Java代码 import ja ...
- Oracle MySQL Hive sql判断字符串中包含某个字符的个数
用length函数求出字符串长度,再对源数据中某个字符去掉求字符串长度,两个长度相减,得出包含某个字符的个数 MySQL所有版本: select length('2022-11-16') - leng ...
最新文章
- 深度学习目标检测详细解析以及Mask R-CNN示例
- 什么样的域名利于网站SEO优化?
- 【WPF】DataGrid多表头的样式设计
- [转载] Java静态绑定与动态绑定
- SQL语法--DML
- ArcGIS 利用全局(局域)空间自相关分析进行城市不同家庭收入情况的空间集聚分析
- 怎么查看笔记本内存条型号_查看笔记本内存条型号
- 当moba遇上麻将——刀塔自走棋
- 一个完整的研发体系应该包括的内容
- css:网页引入字体@font-face以及动态加载字体
- 安卓webview长按分享,长按选择,长按复制,仿好奇心日报长按分享自定义弹窗的实现
- 小程序预览报错60001,fail error:109
- 深度学习深度信念网络DBNs—简易详解
- 计算机网络实验二静态路由基础
- 一次光纤线路故障导致的光纤猫信号灯异常用户无法上网的故障解决过程
- 移动硬盘 双linux系统安装教程,一种单移动硬盘安装多个操作系统的方法与流程...
- 【回忆 总结】我的大学四年
- Can‘t locate CPAN.pm in @INC
- 基于Mahout实现协同过滤推荐算法的电影推荐系统
- GMap.NET入门详细教程【4】--------为控件添加事件,在鼠标单击时打点
热门文章
- java ThreadLocal用法及原理
- 【Irrlicht鬼火引擎】 安装配置Irrlicht鬼火引擎
- call dword prt[eax]
- 面向对象软件设计——设计模式学习
- IBM HACMP 系列 -- 后期安装工作和管理任务二
- 把自己的思想记录下来
- 应用程序性能分析利器 —— Visual Studio Profiler
- Project 2007如何打开项目向导
- 不用CMake编译查询OpenCV函数、类定义等的方法(全文搜索-所有文档搜索指定内容神器:CJC超级硬盘快搜索和AnyTXT Searcher)
- 广西大学计算机科学与技术中法,广西大学