【剑指offer】整数中1出现的次数

转载请注明出处:http://blog.csdn.net/ns_code/article/details/27563485

题目描写叙述:

亲们!!

我们的外国友人YZ这几天总是睡不好,初中奥数里有一个题目一直困扰着他,特此他向JOBDU发来求助信,希望亲们能帮帮他。问题是:求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包括1的数字有1、10、11、12、13因此共出现6次,可是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,能够非常快的求出随意非负整数区间中1出现的次数。

输入:

输入有多组数据,每组測试数据为一行。

每一行有两个整数a,b(0<=a,b<=1,000,000,000)。

输出:

相应每一个測试案例,输出a和b之间1出现的次数。

例子输入:
0 5
1 13
21 55
31 99
例子输出:
1
6
4
7

思路:我们先写一个函数。求出从1到整数n之间1出现的次数。而后分别将要求输入的两个数(具体说,应该是最大的数,和最小的数减去1)作为參数传入该函数,得到的值相减,就可以得到二者之间的的数中1出现的次数。

最简单的方法,分别求从1到n之间每一个数中的1的个数。因为整数n的位数为O(logn),我们要推断一个数有多少个1,须要推断其每一位是否为1,这样一个数就须要推断O(logn)次,而总共同拥有n个数须要求,那么该方法的时间复杂度为O(nlogn)。在九度OJ上用该方法写的代码測试。会超时。

剑指offer上给了一种递归的思路,能将时间复杂度降到O(logn),总感觉这个思路有点偏,并且非常难想到,我没细致看。

我的想法是各位分开统计,并且看到何海涛博客以下非常多人留言,也用了这个方案。就看了下具体的思路,并自己推导了下公式。写出了代码。感觉这个方案还是非常nice的,直观易懂。并且代码简洁,时间复杂度同为O(logn)。

这个方案的思路大概是这种(懒得动手打了,直接copy):

按每一位来考虑,

1)此位大于1。这一位上1的个数有 ([n / 10^(b+1) ] + 1) * 10^b
    2)此位等于0,为 ([n / 10^(b+1) ] ) * 10^b
    3)此位等于1,在0的基础上加上n mod 10^b + 1

举个例子:

30143:

因为3>1,则个位上出现1的次数为(3014+1)*1

因为4>1,则十位上出现1的次数为(301+1)*10

因为1=1,则百位上出现1次数为(30+0)*100+(43+1)

因为0<1。则千位上出现1次数为(3+0)*1000

注:以百位为例。百位出现1为100~199,*100的意思为单步出现了100~199。100次。*30是因为出现了30次100~199,+(43+1)是因为左后一次301**不完整导致。

假设还不懂,自己拿纸和笔大致写下。找下规律,就能推导出来了!

两外,须要注意一点:因为測试系统要求的输入数据最大为1,000,000,000,因此用int会溢出。要用long long。另外比較坑跌的一点是a可能比b大,竟然都没有说明,有点坑了。

AC代码例如以下:

#include<stdio.h>

/*
分别统计num各位上1出现的次数。
相加得到1出现的总次数
*/
long long CountNum1(long long num)
{
	if(num <= 0)
		return 0;

	long long count = 0;	//统计1出现的次数
	long long current;    //当前位
	long long base = 1;	//当前位的基
	long long remain = 0;	//当前位为1时,后面位剩余的数(即不完整的部分)中1出现的次数
	while(num)
	{
		current = num%10;
		num = num/10;

		if(current > 1)
			 count += (num+1)*base;
		else if(current == 1)
			count += num*base + (remain+1);
		else
			count += num*base;

		//下一位要用到的基和剩余不完整部分值
		remain += current*base;
		base *= 10;
	}

	return count;
}

int main()
{
	long long a,b;
	//a,b的大小不定
	while(scanf("%lld %lld",&a,&b) != EOF)
	{
		long long result;
		if(a > b)
			result = CountNum1(a) - CountNum1(b-1);
		else
			result = CountNum1(b) - CountNum1(a-1);

		printf("%lld\n",result);
	}
	return 0;
}

/**************************************************************

    Problem: 1373

    User: mmc_maodun

    Language: C

    Result: Accepted

    Time:0 ms

    Memory:912 kb

****************************************************************/

时间: 2024-08-25 00:38:16

【剑指offer】整数中1出现的次数的相关文章

剑指OFFER 整数中1出现的次数

剑指OFFER 整数中1出现的次数 class Solution { public: int hwo_many(int n) { int count=0; while(n!=0) { if(n%10==1)count++; n/=10; } return count; } int NumberOf1Between1AndN_Solution(int n) { int count = 0; for(int i=1;i<=n;i++){ count += hwo_many(i); } return

剑指offer--32.整数中1出现的次数(从1到n整数中1出现的次数)

暴力挨个数 ---------------------------------------------------------------------- 时间限制:1秒 空间限制:32768K 热度指数:161112 题目描述 求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1.10.11.12.13因此共出现6次,但是对于后面问题他就没辙了.ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出

剑指offer32 整数中1出现的次数(从1到n整数中1出现的次数)

class Solution { public: int NumberOf1Between1AndN_Solution(int n) { if(n<=0) return 0; int count=0; int i=1; while(i<=n) { int p=i; while(p) { if(p%10==1) count++; p=p/10; } i++; } return count; } };

剑指offer 数组中的逆序对

题目描述 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数P.并将P对1000000007取模的结果输出. 即输出P%1000000007 输入描述: 题目保证输入的数组中没有的相同的数字 数据范围: 对于%50的数据,size<=10^4 对于%75的数据,size<=10^5 对于%100的数据,size<=2*10^5 示例1 输入 1,2,3,4,5,6,7,0 输出 7 思路:归并排序的思路.具体参考剑指

剑指 Offer —— 数组中重复的数字

数组中的重复数字 题目描述 牛课网链接 长度为 n 的数组里,所有数字都在 0 到 n-1 的范围内. 数组中某些数字是重复的,但不知道有几个数字是重复的.也不知道每个数字重复几次.请找出数组中任意一个重复的数字. 例如,如果输入长度为 7 的数组{2,3,1,0,2,5,3},那么对应的输出是重复的数字 2 或者 3.(牛课网这里弄成「那么对应的输出是第一个重复的数字 2」了) A 简单实现思路 借助外部数组 b,原数组中的数字记为外部数组的下标,外部数组的值来存储这个数字出现的次数.原数组中

剑指offer-整数中1出现的次数(从1到n整数中1出现的次数)

题目:整数中1出现的次数(从1到n整数中1出现的次数) 求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1.10.11.12.13因此共出现6次,但是对于后面问题他就没辙了.ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数). 思路分析 方法一:统计1-n之间1出现的次数,首先我们可以采用暴力解法直接数,从n到1,对于每个数都进行判断,如果含有1,则计数

剑指offer圆圈中最后剩下的数字 和 迭代器总结

迭代器只有++ ,--,==,!=四种运算方法,不能将iter = iteration+ 1,因为迭代器是指针类型,1是整数类型,不能直接相加赋值给一个指针. 题目描述 每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此.HF作为牛客的资深元老,自然也准备了一些小游戏.其中,有个游戏是这样的:首先,让小朋友们围成一个大圈.然后,他随机指定一个数m,让编号为0的小朋友开始报数.每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下

剑指offer | 链表中倒数第k个结点

先做做一些简单题找找手感,还有赞一个牛客的OJ,这样的提交方式很好,ACM那种对输入出处格式对于没有接触过ACM的人来说比较头疼,华为OJ那种格式描述不清楚的就更头疼了,卡在输入输出上就悲剧了,当然,输入输出也是需要了解的. 顺便吐槽一下华为SO挑战赛:题意有模糊的地方,挑战赛的题目没有区分度,作弊监管太松,判分规则未公开说明. 题目描述:输入一个链表,输出该链表中倒数第k个结点. 题目解析:设两个同步后移.相距k的指针就行,前面的指针触底,后面的指针即是倒数k个节点. 代码如下: 1 /* 2

剑指Offer 通过中序和先序遍历重建二叉树

题目描述 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的数字.例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回. 思路: 写一个reConstruct函数4个形参,分别记录子树前序开始结束的位置,中序开始结束的位子.每次在中序中将前序的根节点找出,讲中序分为前(左子树),后(右子树)2 个部分.递归,直到,子树的开始位置大于结束位置. AC代码: 1 /** 2

剑指Offer——数据流中的中位数

题目描述: 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值.如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值. 分析: 插入排序. 代码: 1 class Solution { 2 public: 3 vector<int> v; 4 int vSize = 0; 5 void Insert(int num) { 6 v.push_back(num); 7 for(int i = vSize - 1; i