【编程马拉松算法目录>>>】


【006-统计一】【工程下载>>>】


1 题目描述


  NewCode总是力争上游,凡事都要拿第一,所以他对“1”这个数情有独钟。爱屋及乌,他也很喜欢包含1的数,例如10、11、12、……。
  例如:N=2,1、2出现了1个“1”。N=12,1、2、3、4、5、6、7、8、9、10、11、12。出现了5个“1”。你能帮他统计一下整数里有多少个1吗?

1.1 输入描述:


  输入有多组数据,每组数据包含一个正整数n,(1≤n≤2147483647)。

1.2 输出描述:


  对应每组输入,输出从1到n(包含1和n)之间包含数字1的个数。例如11与101包含2个1、1101包含3个1。

1.3 输入例子:


1
9
10
20

1.4 输出例子:


1
1
2
12

2 解题思路


2.1 解法一


  最直接的方法就是从1开始遍历到N,将其中每一个数中含有”1”的个数加起来,就得到了问题的解。

private static long countOne3(long n) {long count = 0;for (int i = 0, j; i <= n; i++) {j = i;while (j != 0) {if (j % 10 == 1) {count++;}j = j / 10;}}return count;
}

  此方法简单,容易理解,但它的问题是效率,时间复杂度为Ο(NlgN),N比较大的时候,需要耗费很长的时间。

2.2 解法二


  我们重新分析下这个问题,对于任意一个个位数n,只要n≥1,它就包含一个“1”;n<1,即n=0时,则包含的“1”的个数为0。于是我们考虑用分治的思想将任意一个n位数不断缩小规模分解成许多个个位数,这样求解就很方便。
  但是,我们该如何降低规模?仔细分析,我们会发现,任意一个m位数其值为n(假设这个数为 x1x2x3…xm x_1 x_2 x_3…x_m)中“1”的个位可以分解为两个m-1位数(这两个数是 10m−1 10^{m-1}和 x2x3…xm x_2 x_3…x_m)中“1”的个数的和加上一个与最高位数相关的常数C。例如, f(12)=f(10−1)+f(12−10)+3 f(12)=f(10-1)+f(12-10)+3,其中3是表示最高位为1的数字个数,这里就是10、11、12; f(132)=f(100−1)+f(132−100)+33 f(132)=f(100-1)+f(132-100)+33,33代表最高位为1的数字的个数,这里就是100~132; f(232)=2∗f(100−1)+f(32)+100 f(232)=2*f(100-1)+f(32)+100,因为232大于199,所以它包括了所有最高位为1的数字即100~199,共100个。
  综上,我们分析得出,最后加的常数C只跟最高位 x1 x_1是否为1有关。
  当最高位为1时,常数C为原数字n去掉最高位后剩下的数字+1。
当最高位不为1时,常数C为 10bit 10^{bit},其中bit为N的位数-1,即m-1。如n=12时,bit=1,n=232时,bit=2。
  于是,我们可以列出递归方程如下(其中n= x1x2x3…xm,bit=m−1) x_1 x_2 x_3…x_m,bit=m-1):

f(n)={f(10bit−1)+f(n−10bit)+n−10bit+1x1∗f(10bit−1)+f(n−x1∗10bit)+10bitn≥10,x1=1n≥10,x1≠1)┤

f(n)= \begin{cases} f(10^{bit}-1)+f(n-10^{bit})+n-10^{bit}+1&n≥10,x_1=1\\ x_1*f(10^{bit}-1)+f(n-x_1*10^{bit })+10^{bit}&n≥10,x_1≠1)┤ \end{cases}

  说明:
  1. 对于(1),f(n)(其中 n=x1x2x3…xm n=x_1 x_2 x_3…x_m)可以表示为数字序列(0,1,2,…,n)中1出现的次数,因为最高位为1, n−10bit+1 n-10^{bit}+1表示最高位为1的出现次数(不是1的个数)。对于其它部分1的出现次数还未求出。其它部分成两个部分:
    数字还未到bit位,有 (0,1,2,…,10bit−1) (0,1,2,…, 10^{bit}-1)的数字序列,要求其这个序列1出现的次数,使用 f(10bit−1) f(10^{bit}-1)求得。
    数字已经到了bit位,最高位为1,有数字序列 (10bit,10bit+1,…,x1x2x3…xbit) (10^{bit},10^{bit}+1,…, x_1 x_2 x_3…x_{bit}),因为最高位出现的1的次数已经统计了为 n−10bit+1 n-10^bit+1,所有只要统计其它位出现的次数,实际就是 (0,1,2,…,x2x3…xbit) (0,1,2,…, x_2 x_3…x_{bit})序列中1出现的次数,。
1出现的次数为: f(n)=f(10bit−1)+f(n−10bit)+n−10bit+1 f(n)=f(10^{bit}-1)+f(n-10^{bit} )+n-10^{bit}+1
  2. 对于(2),最高位为1出现的次数为 10bit 10^{bit},不考虑最高位为x_1同时忽略最高位, (0,1,2,…,10bit−1) (0,1,2,…, 10^{bit}-1)序列一共出现了 x1 x_1次,则1出现的次数为 x1∗f(10bit−1) x_1*f(10^{bit}-1)。如果最高位为数字 x1 x_1,则有序列 (0,1,2,…,x2x3…xbit) (0,1,2,…, x_2 x_3…x_{bit}),1出现的次数为: f(n−x1∗10bit) f(n-x_1*10^{bit} )。
1出现的次数为: f(n)=x1∗f(10bit−1)+f(n−x1∗10bit)+10bit f(n)=x_1*f(10^{bit}-1)+f(n-x_1*10^{bit} )+10^{bit}
递归的出口条件为:

f(n)={100<n<10n≤0

f(n)= \begin{cases} 1&0

  综合即为:

f(n)=⎧⎩⎨⎪⎪⎪⎪⎪⎪01f(10bit−1)+f(n−10bit)+n−10bit+1x1∗f(10bit−1)+f(n−x1∗10bit)+10bitn≤00<n<10n≥10,x1=1n≥10,x1≠1

f(n)= \begin{cases} 0&n≤0\\ 1&0

2.3 解法三


  解法二的优点是不用遍历1~N就可以得到f(N)。经过测试,此算法的运算速度比解法一快了许多许多,。但算法二有一个显著的缺点就是当数字超过某个值时会导致堆栈溢出,无法计算
  解法二告诉我们1~N中“1”的个数跟最高位有关,那我们换个角度思考,给定一个N,我们分析1~N中的数在每一位上出现1的次数的和,看看每一位上“1”出现的个数的和由什么决定。
  1位数的情况:在解法二中已经分析过,大于等于1的时候,有1个,小于1就没有。
  2位数的情况:N=13,个位数出现的1的次数为2,分别为1和11,十位数出现1的次数为4,分别为10、11、12、13,所以f(N)=2+4。N=23,个位数出现的1的次数为3,分别为1、11、21,十位数出现1的次数为10,分别为10~19,f(N)=3+10。
  由此我们发现,个位数出现1的次数不仅和个位数有关,和十位数也有关,如果个位数大于等于1,则个位数出现1的次数为十位数的数字加1;如果个位数为0,个位数出现1的次数等于十位数数字。而十位数上出现1的次数也不仅和十位数相关,也和个位数相关:如果十位数字等于1,则十位数上出现1的次数为个位数的数字加1,假如十位数大于1,则十位数上出现1的次数为10。
  3位数的情况:N=123,个位出现1的个数为13:1、11、21、…、91、101、111、121。十位出现1的个数为20:10~19、110~119。百位出现1的个数为24:100~123。
  我们可以继续分析4位数,5位数,推导出下面一般情况:假设N,我们要计算百位上出现1的次数,将由三部分决定:百位上的数字,百位以上的数字,百位以下的数字。
  如果百位上的数字为0,则百位上出现1的次数仅由更高位决定,比如12013,百位出现1的情况为100~199、1100~1199、2100~2199、…、11100~11199,共1200个。等于更高位数字乘以当前位数,即12*100。
如果百位上的数字大于1,则百位上出现1的次数仅由更高位决定,比如12213,百位出现1的情况为100~199、1100~1199、2100~2199、…、11100~11199、12100~12199共1300个。等于更高位数字加1乘以当前位数,即(12+1)*100。
  如果百位上的数字为1,则百位上出现1的次数不仅受更高位影响,还受低位影响。例如12113,受高位影响出现1的情况(等于更高位数字乘以当前位数):100~199、1100~1199、2100~2199、…、11100~11199,共1200个,但它还受低位影响,出现1的情况是12100~12113,共14个,等于低位数字13+1。
  假设n= x1x2x3…xi…xn x_1 x_2 x_3…x_i…x_n,当前处理第i位,f(i)为第i位上1出现的次数则有:

f(n)=⎧⎩⎨⎪⎪(x1…xi−1)∗10n−i(x1…xi−1)∗10n−i+(xi+1…xn+1)(x1…xi−1+1)∗10n−ixi=0xi=1xi>1

f(n)= \begin{cases} (x_1…x_{i-1} )*10^{n-i}&x_i=0\\ (x_1…x_{i-1} )*10^{n-i}+(x_{i+1}…x_n+1)&x_i=1\\ (x_1…x_{i-1}+1)*10^{n-i}&x_i>1 \end{cases}

  [0,n]的序列中1出现的总次数是:

∑i=0nf(i)

\sum_{i=0}^n{f(i)}

3 算法实现


import java.util.Scanner;/*** Author: 王俊超* Time: 2016-05-07 12:14* CSDN: http://blog.csdn.net/derrantcm* Github: https://github.com/Wang-Jun-Chao* Declaration: All Rights Reserved !!!*/
public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);
//        Scanner scanner = new Scanner(Main.class.getClassLoader().getResourceAsStream("data.txt"));while (scanner.hasNext()) {long n = scanner.nextLong();
//            System.out.println(countOne(n));
//            System.out.println(countOne2(n));System.out.println(countOne3(n));}scanner.close();}/*** 统计1出现的次数* 见word解法一** @param n 最大的数字* @return 1出现的次数*/private static long countOne(long n) {long count = 0;for (int i = 0, j; i <= n; i++) {j = i;while (j != 0) {if (j % 10 == 1) {count++;}j = j / 10;}}return count;}/*** 统计1出现的次数* 见word解法二** @param n 最大的数字* @return 1出现的次数*/private static long countOne2(long n) {if (n <= 0) {return 0;} else if (n < 10) {return 1;} else {long count = 0;// 最高位的数字long highest = n;// 数据位数-1int bit = 0;//代表最高位的权重,即最高位一个1代表的大小int power10 = 1;while (highest >= 10) {highest /= 10;bit++;power10 *= 10;}if (highest == 1) {count = countOne(power10 - 1)+ countOne(n - power10)+ n - power10 + 1;} else {count = highest * countOne(power10 - 1)+ countOne(n - highest * power10)+ power10;}return count;}}/*** 【最佳解法】* 统计1出现的次数* 见word解法三** @param n 最大的数字* @return 1出现的次数*/private static long countOne3(long n) {long count = 0;long i = 1;long current = 0, after = 0, before = 0;while ((n / i) != 0) {current = (n / i) % 10;before = n / (i * 10);after = n - (n / i) * i;if (current > 1) {count = count + (before + 1) * i;} else if (current == 0) {count = count + before * i;} else if (current == 1) {count = count + before * i + after + 1;}i *= 10;}return count;}
}

4 测试结果


5 其它信息


因为markddow不好编辑,因此将文档的图片上传以供阅读。Pdf和Word文档可以在Github上进行【下载>>>】。



【编程马拉松】【006-统计一】相关推荐

  1. 编程马拉松大赛试题及代码(C++实现)

    前段时间牛客网举办了编程马拉松大赛,总共86道题,20天内完毕. 题目难度难中易都有.我发现这些题目,主要关注性能和思维. 非常多题目用常规方法是不能通过时间要求的.题目是来自于各大oj以及面试题.所 ...

  2. “百度开放云编程马拉松”中国三大赛区获奖团队及作品新鲜出炉

    2013年7月20-21日,以"绿色生活"为主题,面向广大移动互联网开发者,由百度开发者俱乐部主办.InfoQ和segmentfault社区联办.WWF(世界自然基金会)作为环保公 ...

  3. “百度开放云编程马拉松”武汉赛区25件作品及团队介绍

    7月20日至21日,以"绿色生活"为主题,面向广大移动互联网开发者,由百度公司主办.InfoQ和segmentfault社区联办.WWF(世界自然基金会)作为环保公益合作伙伴的&q ...

  4. 【编程马拉松】【004-包含一】

    [编程马拉松算法目录>>>] [004-包含一][工程下载>>>] 1 题目描述 NowCoder总是力争上游,凡事都要拿第一,所以他对"1"这 ...

  5. 近期活动盘点: Call for Code编程马拉松邀请赛

    想知道近期有什么最新活动?大数点为你整理的近期活动信息在此: 关于代码行动 "代码行动"(Call for Code)是IBM联合Linux基金会.红十字会等组织发起的一项为期5年 ...

  6. 报名 | 赢取20万美金!Call For Code编程马拉松北京站来袭!

    你想通过技术改变未来的世界,并挽救生命吗? 你想组织小伙伴一同参赛,并获得20万美金吗? 你想和全球专业的开发者一同开发,并学习最前沿的AI.IoT等技术吗? 如果你内心中的答案是肯定的话...... ...

  7. 阿里云开源编程马拉松入围项目

    在9月8号启动的 阿里云开源编程马拉松 至今已经吸引了100多个团队参赛.经过评委的初步筛选现有14个参赛项目入选.入选的项目团队/个人 将会在10月14日云栖大会现场进行路演,在大会现场颁奖并评选出 ...

  8. 核桃编程学python吗_学编程要趁早?对话核桃编程X支付宝 “小程序编程马拉松”三强得主...

    原标题:学编程要趁早?对话核桃编程X支付宝 "小程序编程马拉松"三强得主 来源:网络 浏览专业IT技术社区网站,创作沉浸式互动体验游戏,开发辟谣小程序......这并不是程序员大神 ...

  9. 编程马拉松(英语:hackathon,又译为黑客松)

    编程马拉松(英语:hackathon,又译为黑客松),又称黑客日(hack day).黑客节(hackfest)或编程节(codefest),是一个流传于黑客(hacker)当中的新词汇.编程马拉松是 ...

最新文章

  1. 浅谈Java SE、Java EE、Java ME三者的区别
  2. 永远不要在代码中使用「User」这个单词
  3. PMP-【第2章 项目运行环境与项目经理】2021-1-4(49页-60页)
  4. 【cocos2d-x从c++到js】16:使用cocos2d-console工具转换脚本为字节码
  5. boost::mpl模块实现deque相关的测试程序
  6. Istio1.5 Envoy 数据面 WASM 实践
  7. 一组动图看懂3D打印原理
  8. poj Balanced Lineup RMQ
  9. php找不到intl,php_intl.dll找不到指定模块怎么办
  10. Paradigm 已对 NFT 艺术品铸造和收藏协议 ZORA 进行投资
  11. ORACLE 中ROWNUM用法总结(转载)
  12. Atom 扩展离线安装
  13. html中不支持什么元素,html5新元素和html不支持的元素
  14. 清除回收站苦闷纠结欣喜
  15. pg数据库中postgis的使用
  16. QGIS制图:矢量数据制图流程及导出
  17. 福家宝机器人,关于举办第六届宁夏青少年智能机器人竞赛的通知.doc
  18. 企查查爬虫获取公司链接
  19. Python数独算法
  20. img标签无图片或者图片url错误时显示默认图片

热门文章

  1. i7-7700CPU上安装ubuntu16.04以及显卡、网卡不兼容问题的解决
  2. Day108.尚医通:医院模拟系统接口对接 - 医院|科室|排班 增删改分页条件查询
  3. ubuntu虚拟机磁盘扩容
  4. DoDataExchange(CDataExchange *pDX) 详解
  5. 关于对SLAM(视觉)的学习
  6. 这7位年轻人正在让世界颤抖,直接预见未来!
  7. 史上最口语化Arduino入门教程——1.3模拟IO口
  8. 计算机学院教师老带新总结,教师以老带新个人工作总结三篇
  9. bfgs算法matlab程序,bfgs算法matlab代码
  10. innodb组合索引的最左前缀原则