Leetcode NUM1
统计从1到n中1出现的次数?
解题思路一(代码简单思路较难想到):
通过使用一个位置乘子m 遍历数字的位置, m 分别为1,10,100,1000…etc.(m<=n)
对于每个位置来说,把10进制数分成两个部分,比如说 当m=100的时候, 把十进制数 n=3141592 分成 a=31415 和 b=92 ,以此来分析百位数为1时所有数的个数和。m=100时,百位数的前缀为3141,当百位数大于1时,为3142100,因为当百位数大于1时,前缀可以为0,即百位数可以从100到199,共100个数;当百位数不大于1时,为3141100;如何判断百位数是否大于1?假设百位数为x,若(x+8)/10等于1,则大于1,若(x+8)/10等于0,则小于1。因此前缀可用(n/m + 8)/10 m来计算(若计算2的个数,可以改为(n/m + 7)/10m,若计算3的个数,改为(n/m + 6)/10*m,…以此类推)。
再例如m=1000时,n分为a=3141和 b=592;千位数的前缀为314,千位数不大于1,故前缀计算为3141000;因为千位数为1,再加b+1(0到592)。即千位数为1的所有书的个数和为3141000+592+1;公式(n/m + 8)/10*m + b +1。
注意:只有n的第m位为1时需要计算后缀,后缀计算为 (n/m%10==1)*(b+1),
即(n/m%101)判断第m位是否为1,若为1,则加上(b+1),若不为1,则只计算前缀。(若计算2的个数,可以改为(n/m%102)(b+1),若计算3的个数,可以改为(n/m%10==3)(b+1)…以此类推)
代码:
int NumberOf1Between1AndN_Solution(int n)
{
int cnt = 0;
int a=0,b=0;
for(long long m=1;m<=n;m*=10){
a = n/m;
b = n%m;
cnt+=(a+8)/10*m + (a%10==1)*(b+1);
}
return cnt;
}
解题思路2:
出现1无非是在个位、十位、百位、千位。。。等出现,那对一个数判断是否有1便是判断他的各个位有没有1出现。
例子:
(1)个位上出现1的数有什么规律呢,个位出现1的数可写作x10+1, 则有 ((x10+1)-1)%10<1.
(2)十位上出现1的数又有什么规律呢,十位出现1的数可写作 x100+10+y,(y<10),则有 ((x100+10+y)-10)%100=y<10.
(3)再来,百位出现1的数字有什么规律呢,百位出现1的数可写作x1000+100+y,y<100,则有((x1000+100+y)-100)%1000=y<100
总结:举例子只是为了方便理解,统一的来说,当10的k次方出现的1的数都可以写作,number=x*10(k+1)+10k+y,其中y<10k,则恒有(number-10k)%10(k+1)<=y<10k
方法:对一个数是否满足条件来说,先求出该数的位数length,然后遍历k=1->length,判断是否有满足上式的k值存在,若存在,则该数符合条件。
代码:
class Solution {
public:
int NumberOf1Between1AndN_Solution(int n)
{
int res = 0, length;
for (int i = 1; i <= n; i++) {
length = 0;
while (i >= pow(10, length)) {
if (((i -(int) pow(10, length)) % (int)pow(10, length + 1))<pow(10, length))
res++;
length++;
}
}
return res;
}
};