题目描述
试计算在区间 1 到 n的所有整数中,数字x (0≤x≤9)共出现了多少次?
例如,在 1到11中,即在 1,2,3,4,5,6,7,8,9,10,11 中,数字 1 出现了 4 次。
输入
2个整数n, x,之间用一个空格隔开。
输出
1个整数,表示x出现的次数。
分析
最简单的暴力循环,非常耗时:
优化方案:
可以逐位分析,比如输入6573,5时。逐位分析如下:(k为从右往左数第几位)
- k=0时,???5 ,前三位可取范围为:[0,656] 共657种;高位恰好为657
- k=1时,??5? ,前两位可取范围为:[0,65] 66种;最后一位可取范围为:[0,9]共10种,所以共计660种
- k=2时,?5??,
3.1 前1位取范围:[0,5],后两位可取范围为:[0~99],可取范围为 6 ∗ 1 0 2 = 600 6*10^2=600 6∗102=600种;
3.2 前1位取6时,即65??,后两位可取范围位[0~73]共74种
3.3 综上,此时可取范围为674种 - k=3时,5???,后三位可取 1 0 3 = 1000 10^3=1000 103=1000种
- 所以最终结果为:657+660+674+1000=2991种
从上述推导可以尝试推出一般规律,高位取值范围(当前位左边的数字)取决于当前位(cur)和输入x的相对大小,当输入n为6573时,以计算十位为例,cur=7(十位数字),此时cur左边高位 high=65,k=1(右边第一位,从0开始)
- cur<x时;假设x=8,??8?, 高位只能取[0,64],共65种,即high种,得到: a n s = ( h i g h ) ∗ 1 0 k = 650 ans=(high)∗10^k=650 ans=(high)∗10k=650
- cur>x时;假设x=5, ??5? , 高位可取[0,65],共66种,即high+1种, 低位10种,得到: a n s = ( h i g h + 1 ) ∗ 1 0 k = 660 ans=(high+1)∗10^k=660 ans=(high+1)∗10k=660
- cur==x时;假设x=7, ??7?, 此时分两种计算: 高位取 [0,64],低位取0~9;高位取65,即 657?,低位取[0,3]共4种。得到: a n s = h i g h ∗ 1 0 k + n ans=high*10^k+n ans=high∗10k+n% 1 0 k ( 低位 ) + 1 = 65 ∗ 10 + 1 ∗ ( 3 + 1 ) = 654 10^k(低位)+1=65*10+1*(3+1)=654 10k(低位)+1=65∗10+1∗(3+1)=654
- 特别的,考虑输入x为0的情况, ??0?,按照情形2(cur>x)之前分析,高位取[0,65]共66种,实际上此处不能取0,如果取0则变成了000?,去掉高位的0最后取到的数为0 ~ 9,很明显 1 ~ 9不包含0不符合题意,数字0虽然包含0,但题目要求范围是[1,n],所以此时高位可取范围为[1,65]共65种, 即需要在上面的情况下-1;
解决方案
备注:如果理解了上面的分析过程,那么不用看代码注释。
#include <stdio.h>
#include <math.h>
int func(int n,int x){
int ans=0;
int tmp=n; //备份n,变更tmp不改变n;
int high,mi,cur,k=0;//k代表从右往左数第几位,从0开始数
while(tmp){
cur=tmp%10; //cur 代表当前位数,从个位开始
tmp=tmp/10; // tmp为商,比如最开始tmp为32487,当k=1时,tmp=324,cur=8;
//分三种情况讨论(下例均讨论计算十位的情况,即k=1):
//1. 当cur<x时:输入n=32487,x=9;当k=1时,tmp=324,cur=8,要想十位为9,前面三个数的范围只能为[0,323]共324种,个位数为0-9共十种, ==》 tmp*10^k=3240种
//2. 当cur>x时:输入n=32487,x=7;当k=1时,tmp=324,cur=8,要想十位为7,前面三个数的范围为[0,324]共325种,即tmp+1种;个位同上十种情况,==》 (tmp+1)*10^k=3250
//3. 当cur==x时:输入n=32487,x=8;当k=1时,tmp=324,cur=8,要想十位为8,前面三个数取[0,323]时,得到 tmp*10^k=3240种,当前三位数为324时,即3248?,cur右边的最大值为7,共8种情况(0~7),==》 tmp*10^k+n%(10^k)=3247种;
//4. 考虑特殊情况(x输入0时),当n=32487,x=0;当k=1,tmp=324,cur=8,按照前面的讨论2,cur>x,那么 ???0?,前三位取值范围应该为[0,324]共325种,实际上此时不能取0,如果取0,则变成了0000?,去掉首位的0,得到的结果是0~9,1~9明显不包含0不符合题意,0虽然包含0,但题目要求是[1,n],故舍去。即此时前三位取值只能是[1,324]共324种情况
high=tmp; //不同情况需要的tmp不同,但是tmp作为最外层循环条件,不应该改变,所以另外给一个变量来计算
if(x==0) high--;
if(cur>x) high++;
mi=pow(10,k); //pow默认返回double,可以使用(int)强制转换
ans+=high*mi;
if(cur==x) ans+=n%mi+1;
k++;
}
return ans;
}
int main(){
int n,x;
scanf("%d%d",&n,&x);
printf("%d\n",func(n,x));
return 0;
}
效率明显好于暴力循环
小结
这类问题咋一看很复杂,其实只要静下心来仔细分析,也不是什么难题。就是高中数学的基本组合思维。工作中太久不用,生疏了而已