程序员面试金典——解题总结: 9.18高难度题 18.4编写一个方法,数出0到n(含)中数字2出现了几次

#include <iostream>
#include <stdio.h>

using namespace std;
/*
问题:编写一个方法,数出0到n(含)中数字2出现了几次
分析:剑指offer中,题目是数出0到n中数字1出现了几次。应该是同一个类型
      记得当时是用递归做的。
	  暴力破解:就是遍历0到n,对每个数字i,获取其每一位上的值,统计是否含有2,假设平均每个数字有k位,时间复杂度就是O(n*k)。
	  另一种是采用递归的方式,统计出每种模式下,比如n有k位,那么计算所有k-1位的上的2的个数,而k-1位上的2的个数
	  以一个简单的例子讲解:十位数是个位数前面加上一个1~9,比如10~99,是对个位数0~9中每个前面分别加上1~9
	  注意百位数有区别,比如:100~999,是对所有十位数10~99前面分别加上1~9,这样还会漏掉:100~109,所以要把百位数最小值100~添加后的最小百位数110-1进行累加

输入:
1000(n) 1(统计的值)
21345 1
1000 2
21345 2
输出:
301
18821
300
9821

关键:
1 以统计1到n中1出现的次数为例分析(与统计1到n中2出现的次数是一样的题目)
  以21345为例进行拆分,拆分成低位部分:1~1345和高位部分1346~21345,
  设整数strN,最高位为第i位,值为k
  1】先统计整数strN仅包含最高位部分: 发现如果最高位k=1(例如11345),则1的个数等于后面低位部分:1345+1,即为 atoi(strN+1) + 1
                                                    k>1(例如21345),则1的个数等于: k*10^(i-1)
  2】统计不包含最高位的低位部分中,1的出现次数,发现1出现的(例如21345)的次数 = k * 10^(i-2),然后遍历剩余i-1个低位中,设置任意低位为1
  则总的次数 =(i-1)* k * 10^(i-2)
  3】递归对低位部分进行处理,记总次数为num
  1的个数= 第i位为1则为(剩余位值+1) 或 第i位不为1则为(第i位的值 * 10的(i-1)次方) + 除高位外次最高位中1的个数(高位的值 * 10的(i-2)次方 * (i-1)) + 递归得到低位中含1的个数

2 
int countNumber(const char* strN , int searchValue)
{
	if(NULL == strN || (*strN) < '0' || (*strN) > '9')
	{
		return 0;
	}
	//获取长度
	int length = strlen(strN);
	int firstDigit = *strN - '0';
	//如果是0
	if(1 == length && firstDigit < searchValue)
	{
		return 0;
	}
	if(1 == length && firstDigit >= searchValue)
	{
		return 1;
	}
	将数n分成三部分:假设数n有i位,则
	分成:低位部分(数n的第1位到第i-1位),高位部分
	比如21345分成:1~1345,1346~21345(不含最高位的低位部分)
	1的个数= 第i位为1则为(剩余位值+1) 或 第i位不为1则为(第i位的值 * 10的(i-1)次方) + 除高位外次最高位中1的个数(高位的值 * 10的(i-2)次方 * (i-1)) + 递归得到低位中含1的个数
	int numberHigh = 0;
	int numberOther = 0;
	int numberRecursive = 0;
	//注意这里是大于1,而不是不等于1
	if(firstDigit > searchValue)
	{
		//这里不要乘以个数firstDigit
		numberHigh = powerBase10(length - 1);
	}
	else if(firstDigit == searchValue)
	{
		numberHigh = atoi(strN + 1) + 1;
	}
	numberOther = firstDigit * (length - 1) * powerBase10(length - 2); //为什么要乘以length-1,就是说从剩余length-1位中任选一位为1,first * power(lenght-2)决定了除高位剩余位的元素数量
	//numberOther = firstDigit * powerBase10(length - 2);//
	numberRecursive = countNumber(strN + 1, searchValue);
	int result = numberHigh + numberOther + numberRecursive;
	return result;
}
*/

const int MAXSIZE = 1024;

int powerBase10(int times)
{
	int result = 1;
	for(int i = 0 ; i < times ; i++)
	{
		result *= 10;
	}
	return result;
}

int countNumber(const char* strN , int searchValue)
{
	if(NULL == strN || (*strN) < '0' || (*strN) > '9')
	{
		return 0;
	}
	//获取长度
	int length = strlen(strN);
	int firstDigit = *strN - '0';
	//如果是0
	if(1 == length && firstDigit < searchValue)
	{
		return 0;
	}
	if(1 == length && firstDigit >= searchValue)
	{
		return 1;
	}
	/*
	将数n分成三部分:假设数n有i位,则
	分成:低位部分(数n的第1位到第i-1位),高位部分
	比如21345分成:1~1345,1346~21345(不含最高位的低位部分)
	1的个数= 第i位为1则为(剩余位值+1) 或 第i位不为1则为(第i位的值 * 10的(i-1)次方) + 除高位外次最高位中1的个数(高位的值 * 10的(i-2)次方 * (i-1)) + 递归得到低位中含1的个数
	*/
	int numberHigh = 0;
	int numberOther = 0;
	int numberRecursive = 0;
	//注意这里是大于1,而不是不等于1
	if(firstDigit > searchValue)
	{
		//这里不要乘以个数firstDigit
		numberHigh = powerBase10(length - 1);
	}
	else if(firstDigit == searchValue)
	{
		numberHigh = atoi(strN + 1) + 1;
	}
	numberOther = firstDigit * (length - 1) * powerBase10(length - 2); //为什么要乘以length-1,就是说从剩余length-1位中任选一位为1,first * power(lenght-2)决定了除高位剩余位的元素数量
	//numberOther = firstDigit * powerBase10(length - 2);//
	numberRecursive = countNumber(strN + 1, searchValue);
	int result = numberHigh + numberOther + numberRecursive;
	return result;
}

//获取一个整数value上searchValue值得个数
int countValue(int value , int searchValue)
{
	int result = 0;
	int count = 0;
	while(value)
	{
		result = value % 10;
		if(result == searchValue)
		{
			count++;
		}
		value /= 10;
	}
	return count;
}

int count(int n , int value)
{
	int sum = 0;
	int result = 0;
	for(int i = 1 ; i <= n ; i++)
	{
		result = countValue(i , value);
		sum += result;
	}
	return sum;
}

void process()
{
	char strN[MAXSIZE];
	int searchValue = 1;
	while(scanf("%s %d", strN , &searchValue))
	//while(scanf("%s", strN))
	{
		int result = countNumber(strN , searchValue);
		int n = atoi(strN);
		int result2 = count(n , searchValue);
		cout << result << endl;
		//cout << result2 << endl;
		memset(strN , 0 , sizeof(strN));
	}
}

int main(int argc , char* argv[])
{
	process();
	getchar();
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值