题目描述
求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。
设计思想一:
最容易想到的:每次对10求余,判断个数是否为1,再将原数除以10作为下次除以10求余的输入
int NumberOfBetweenAndN(unsigned int n)
{
int number=0;
for(unsigned int i=1;i<=n;++i)
number+=NumberOf1(i);
retrun number;
}
int NumberOF1(unsigned int n)
{
int number=0;
while(n)
{
if(n%10==1)
number++;
n=n/10;
}
return number;
}
法一的时间复杂度分析:对每个数字n,有O(logn)位;从1~n有n个数,所以总的时间复杂度O(nlogn),如果n很大,会很耗时。
法二:
先看最高位出现1的次数:
以21345为例,万位的的1出现在10000~19999,共10^4个,但是如果n的最高位不是1的话,比如12345,万位的1只能出现在10000~12345,共2345+1个。所以最高位是1的数,要分输入数字n的最高位是大于1,等于1,还是小于1来计算:大于1 时候,假设最高位是first,则最高位是1的数字个数NumOfFirst1=10^(n的位数-1);若first==1,则NumOfFirst1=(n-10000)+1
再分析次高位至最低位:
仍以21345为例,若是5位数,考虑低四位,在1346~11345和11346~21345这两段,每一段除万位外,1的出现次数是4*10^3=4000(选定其中1位是1,另外3位可任选,就是一个排列组合),两段就是8000
接下来考虑1~1345,可以用递归,这也是拆出1346~11345和11346~21345这两段的原因
#include <cmath>
#include <string.h>
class Solution {
public:
int NumberOf1(const char* p)
{
if(*p<'0'||*p>'9'||!p||*p=='\0')
return 0;
int first=*p-'0';
unsigned int length=static_cast<unsigned int>(strlen(p));
if(first==0&&length==1)
return 0;
if(length==1&&first>0)
return 1;
int numberoffirstdigit=0;
if(first>1)
numberoffirstdigit=pow(10,length-1);
else if(first==1)
numberoffirstdigit=atoi(p+1)+1;
int othernum=first*pow(10,length-2)*(length-1);
int numRescursive=NumberOf1(p+1);
return numRescursive+numberoffirstdigit+othernum;
}
int NumberOf1Between1AndN_Solution(int n)
{
if(n<=0)
return 0;
char str[50];
sprintf(str,"%d",n);
return NumberOf1(str);
}
};
关于本文用到的sprintf()的用法:https://blog.csdn.net/qq_34793133/article/details/81261178