题目描述:
输入一个整数N,求出1~N这N个整数的十进制表示形式中 1出现的次数。比如输入12,从1~12这12个整数中包含1的数字有:1,10,11,12,1总共出现了5 次。
题目分析:
看到这个题目,大多数人的想法或许就是:循环遍历1~N中的每一个数,然后求出每个数中1的个数,然后求和。这样做的话,需要将N个数遍历一遍,然后将每个数的每一位遍历一次,时间复杂度是比较高的(大于O(N)),所以我们依然找规律去做这个题目。
假如给定的数据是24531.
我们可以将数据的范围(1~24531)分成2部分1~4531和4532~24531.然后求出每一个范围中1的个数,将结果进行相加即可。
在4532~24531中最高位出现1的个数,一共就是10000(10000~19999)个.
如果我给定的数据是12345,最高位出现1的个也是10000吗?显然不是。而是2345+1 = 2346(10000~12345)个。
那么剩下四位中1出现的个数又该如何计算呢?2*4*10^(5-2) == 8000个。
这个范围可以再分为2个范围,4532~14531和14532~24531.对于低4位,取任意一位是1(4种取法),其余3位就有10*10*10 == 1000种可能。
在1~4531这个范围中,我们就可以继续上边的步骤(递归实现)。
实现代码:
#include<iostream>
using namespace std;
#include<cassert>
//1~N的十进制表示中出现的次数
int Power(int n)
{
int result = 1;
for(int i = 0; i < n; ++i)
{
result *= 10;
}
return result;
}
int numberOf1(const char* str)
{
if(*str < '0'|| *str > '9' || *str == '\0' || str == NULL)//字符串不正确,进而说明数据有误
return 0;
int length = strlen(str);
int first = *str - '0';
if(first == 0 && length == 1)//处理这种情况
return 0;
if(first > 0 && length == 1)//处理N是个位数的情况
return 1;
//假如给定的数是 24531 数字分为两部分 1~4531 和4532 ~ 24531
//1,2计算的是~ 24531中的个数
//3计算的是1~ 4531中的个数
//1.计算~19999中出现的次数
int CountFirstDigit = 0;
if(first > 1)
CountFirstDigit = Power(length-1);
if(first == 1)
CountFirstDigit = atoi(str+1)+1;
//2.计算后面位中出现的次数
int CountOtherDigits = first * (length -1) * Power(length - 2);
//3.计算~4531中出现的个数
int CountRest = numberOf1(str + 1);
return CountFirstDigit+ CountOtherDigits + CountRest;
}
int GetCount(int n)
{
if(n <= 0)
return 0;
char str[15];
_itoa(n,str,10);//将输入的数字转为字符串,为了方便编写程序
return numberOf1(str);
}
int main()
{
int n = 0;
cin>>n;
cout<<GetCount(n)<<endl;
system("pause");
return 0;
}
总结:
这里我们之所以将数字转为字符串,是为了方便表示数字,当然不转换也是可以的,只是表示起来或许比较麻烦。
整数转换成字符串的函数:char* _itoa(int n,char* string,int radix);
n表示要转换的整数,转换之后的字符串存储在string中,radix是一个整数,表示n是radix进制的整数。
当然也可以使用sprintf函数进行转换。
int sprintf(char* buffer,const char* format…);
sprintf(buffer,”%s”,n);表示n以%d的形式写进buffer中,返回值是写进去的字节数。
关于这道题目就先分析到这里~