题目:输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数。例如输入12,从1到12这些整数中包含1 的数字有1,10,11和12,1一共出现了5次。
解: 由题意可知,所求的1的数量,就是1在数字n中的各个位数上出现的次数。
所以,设数n为abc,求1在abc三个位置数出现的次数。
求c位:当c>1时,ab位为00..ab,即c位1的出现次数为ab+1;
当c=1时,ab位为00..ab,即c位1的出现次数为ab+1;
当c<1时,ab位为00..ab-1,即c位1的出现次数为ab;
求a位:当a>1时,bc位为00..99,即a位1的出现次数为100;
当a=1时,bc位为00..ab,即c位1的出现次数为bc+1;
当a<1时,bc位为00,即c位1的出现次数为0;
求b位:当b>1时,a位0..a,c位0..9,即1的次数(a+1)*10;
当b=1时,a位0..a, 其中
a位0,c位只能是0..c,-> c+1次
a位1..a时,c位为0..9,->a*10次
即1的次数为a*10+c+1
当b<1时,a位1..a,c位0..9,即1的次数位a*10
由上可看出,计算中间位是最复杂的,左右俩边都是很简单的。那么能不能把计算俩边的算式和计算中间位一样呢。试试看:
设左边位为left, 右边位为right, 中间位为mid, 中间位就是计算次数的当前位置, 1的次数为sum
当mid=b时,left=a,right=c
则:mid>1 -> sum=(left+1) * 10^1
mid=1 -> sum=left * 10^1 + right + 1
mid<1 -> sum=left * 10^1
当mid=c位时,left=ab,right=0
则:mid>1 -> sum=(left+1) * 10^0;
mid=1 -> sum=left * 10^0 + right +1;
mid<1 -> sum=left * 10^0;
当mid=a时,left=0, right=bc
则:mid>1 -> sum=100=10^2=(left+1) * 10^2;
mid=1 -> sum=right+1=left * 10^2 + right + 1;
mid<1 -> sum=0=left * 10^2;
显然abc三个位置的计算算式可以是一样的,只数字所在的位置的指数不一样,百位2,十位1,个位0
int count_digital(int digit, int tag=1){
int left, mid, right;
int sum = 0;
int n = 1;
while (digit / n > 0){
right = digit % n;
left = digit / (n * 10);
mid = digit / n % n;
if (mid > tag){
sum += (left+1) * n;
} else if (mid == tag){
sum += left * n + right + 1;
} else { //if (mid < tag) {
sum += left * n;
}
n *= 10;
}
return sum;
}
考虑计算数字0的次数情况:
当tag=0时,是对mid==0进行计次,此时left不能出现0的情况。例如对102进行这种计算,则百位1不可能出现0的情况,因为001..099,这个0已经不符合题意了。对十位进行0的次数计算时,left只能取1,left取0也不符合题意了。
所以以上代码的运行结果会错误。
所以添加判断条件flag
int count_digital(int digit, int tag=0){
int left, mid, right;
int sum = 0;
int flag = 0;
int n = 1;
if (0 == tag) {
flag = -1;
}
while (digit / n > 0){
right = digit % n;
left = digit / (n * 10);
mid = digit / n % n;
if ((tag==0) && (left==0)) break;
if (mid > tag){
sum += (left+flag+1) * n;
} else if (mid == tag){
sum += (left+flag) * n + right + 1;
} else { //if (mid < tag) {
sum += left * n;
}
n *= 10;
}
return sum;
}
解: 由题意可知,所求的1的数量,就是1在数字n中的各个位数上出现的次数。
所以,设数n为abc,求1在abc三个位置数出现的次数。
求c位:当c>1时,ab位为00..ab,即c位1的出现次数为ab+1;
当c=1时,ab位为00..ab,即c位1的出现次数为ab+1;
当c<1时,ab位为00..ab-1,即c位1的出现次数为ab;
求a位:当a>1时,bc位为00..99,即a位1的出现次数为100;
当a=1时,bc位为00..ab,即c位1的出现次数为bc+1;
当a<1时,bc位为00,即c位1的出现次数为0;
求b位:当b>1时,a位0..a,c位0..9,即1的次数(a+1)*10;
当b=1时,a位0..a, 其中
a位0,c位只能是0..c,-> c+1次
a位1..a时,c位为0..9,->a*10次
即1的次数为a*10+c+1
当b<1时,a位1..a,c位0..9,即1的次数位a*10
由上可看出,计算中间位是最复杂的,左右俩边都是很简单的。那么能不能把计算俩边的算式和计算中间位一样呢。试试看:
设左边位为left, 右边位为right, 中间位为mid, 中间位就是计算次数的当前位置, 1的次数为sum
当mid=b时,left=a,right=c
则:mid>1 -> sum=(left+1) * 10^1
mid=1 -> sum=left * 10^1 + right + 1
mid<1 -> sum=left * 10^1
当mid=c位时,left=ab,right=0
则:mid>1 -> sum=(left+1) * 10^0;
mid=1 -> sum=left * 10^0 + right +1;
mid<1 -> sum=left * 10^0;
当mid=a时,left=0, right=bc
则:mid>1 -> sum=100=10^2=(left+1) * 10^2;
mid=1 -> sum=right+1=left * 10^2 + right + 1;
mid<1 -> sum=0=left * 10^2;
显然abc三个位置的计算算式可以是一样的,只数字所在的位置的指数不一样,百位2,十位1,个位0
int count_digital(int digit, int tag=1){
int left, mid, right;
int sum = 0;
int n = 1;
while (digit / n > 0){
right = digit % n;
left = digit / (n * 10);
mid = digit / n % n;
if (mid > tag){
sum += (left+1) * n;
} else if (mid == tag){
sum += left * n + right + 1;
} else { //if (mid < tag) {
sum += left * n;
}
n *= 10;
}
return sum;
}
考虑计算数字0的次数情况:
当tag=0时,是对mid==0进行计次,此时left不能出现0的情况。例如对102进行这种计算,则百位1不可能出现0的情况,因为001..099,这个0已经不符合题意了。对十位进行0的次数计算时,left只能取1,left取0也不符合题意了。
所以以上代码的运行结果会错误。
所以添加判断条件flag
int count_digital(int digit, int tag=0){
int left, mid, right;
int sum = 0;
int flag = 0;
int n = 1;
if (0 == tag) {
flag = -1;
}
while (digit / n > 0){
right = digit % n;
left = digit / (n * 10);
mid = digit / n % n;
if ((tag==0) && (left==0)) break;
if (mid > tag){
sum += (left+flag+1) * n;
} else if (mid == tag){
sum += (left+flag) * n + right + 1;
} else { //if (mid < tag) {
sum += left * n;
}
n *= 10;
}
return sum;
}