这个题目暴力解决貌似可以通过,但时间复杂度太大了。
法一 剑指offer版
比如数字21345,可以把他分为2段,1-1345 1346-21345
先看1346-21345,因为最高位是2,大于1,因此,只看最高位的话,1出现的次数就是10000-19999,即10000次
看低位,我们发现包括1346-9999 0000-9999 0000-1345这3部分,其实也就是求2个 0000-9999中1出现的次数,我们可以总结规律:
在4位数中,任选一位是1,其余三位可以在0-9这10个数字中任意选择,因此根据排列组合的原理,出现的次数为4*10^3=4000,再乘以2即800。至于为什么可以用排列组合,可以这么考虑:任选的一位比如1000,即选择第一位即千位是1,那么千位是1的数字个数为10^3,那么如果任选的是百位,那么百位是1的个数也是10^3,依次共有4中选择,所以10^3.
至于1-1345中出现的次数,可以用递归来解决。
因此,这可以看作一个递归问题
char strn[50];
int numberof1(const char *s)
{
if(!s||*s=='\0')
return 0;
int first=*s-48;
int length=strlen(s);
int res_count=0;
if(first==0&&length==1)
return 0;
if(length==1&&first>0)
return 1;
int numberfirstdigit=0;
if(first>1)
numberfirstdigit=int(pow(10,length-1));
else if(first==1)
numberfirstdigit=atoi(s+1)+1;
int numberotherdigit=first*(length-1)*int(pow(10,length-2));
int numrecursive=numberof1(s+1);
return numberfirstdigit+numberotherdigit+numrecursive;
}
int NumberOf1Between1AndN_Solution(int n)
{
if(n<=0)
return 0;
int i=0;
while(n)
{
strn[i++]=n%10+48;
n=n/10;
}
strn[i]='\0';
int k=0,m=i-1;
while(k<m)
{
swap(strn[k],strn[m]);
k++;
m--;
}
return numberof1(strn);
}
法二
这一版思路比较清晰。
比如数字13524。
我们可以考虑百位,百位是5,大于等于2,那么百位是1的数就有100-199,1100-1199,2100-2199,3100-3199 ......10100-10199,11100-11199,12100-12199,13100-13199,则百位共有(13+1)*100个1,13是百位前的数字
再看数字13110
考虑百位,百位是1,小于2,那么百位是1的数就有100-199,1100-1199,2100-2199,......10100-10199,11100-11199,12100-12199,13100-13110,则百位共有13*100+10+1个,13是百位前的数字,10是百位后的数字
再看数字13011
百位是0,那么百位是1的数字有100-199,1100-1199,2100-2199......10100-10199,11100-11199,12100-12199,则百位共有13*100个1,13是百位前的数字。
因此,某位上出现1的次数可能会受到该位高位数字和低位数字的影响。
int NumberOf1Between1AndN_Solution(int n)
{
int numof1=0;
int current=0,before=0,after=0;
int i=1; //从个位算起
while(n/i) //如果该位上有数字,即该位存在
{
current=n/i%10;
before=n/(i*10);
after=n%i;
if(current>=2)
{
numof1+=(before+1)*i;
}
else if(current==1)
numof1+=before*i+after+1;
else
numof1+=before*i;
i=i*10;
}
return numof1;
}
注意怎么计算高位低位(不一定是单位数)和当前位(是一个0-9的数字)
法三
最简单的方法
对于数字3141592
先看个位数,为2,大于1,那么置个位数为1,前边的数字可以从0-314159之间随意排列,所以共有314160种可能(即个位数为1的情况数)
再看十位数,为9,大于1,那么置十位数为1,前面的数字可以从0-31415之间随意排列,十位数后面的数字也可以选择0-9,因此共有314160种可能。
再看百位,为5,大于1,置百位为1,前面的数字从0-3141之间随意排列,百位后面的数字也可以从0-99,因此共有314200种可能
再看千位,为1,比较特殊,虽然前面的数字可以从0-314之间随意排列,但当是314时,后面的范围只可以时0-592,当为0-313时,后面的范围可以是0-999因此需要分开,即314*1000+593=314593
后面依次。。。
由于当某一位数字大于等于2时,该位为1时出现的情况不需要特殊处理,(该位前的数字+1)*位数值即可
当某一位数字小于2时,该位为1时出现的情况需要进行处理,即(该位前的数字)*位数值+(该位后的数字+1)*t(当该位数字为1时t=1,为0时t=0)
那如何判断大于还是小于2,可以采用加8看是否进位即可,如果进位,说明大于等于2,按照进位后的数据乘以位数值即可,如果没有进位,处理完前段再处理后段即可(处理后段时需要判断该位是为0还是为1)
int NumberOf1Between1AndN_Solution(int n)
{
int numof1=0;
if(n<=0)
return 0;
for(long long m=1;m<=n;m*=10)
{
int a=n/m,b=n%m;
numof1+=(a+8)/10*m+(b+1)*(a%10==1);
}
return numof1;
}