剑指offer 从1到n整数中1出现的次数
题目:
输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数。
例如输入12,从1到12这些整数中包含“1”的数字有1,10,11和12,其中“1”一共出现了5次。
样例
输入: 12
输出: 5
思路:求整数累加每一位上1出现的次数。核心问题转变为确定一位上1出现的次数。
情况分为三类:
- 当前位值为0
- 当前位值为1
- 当前位值大于1
例子: 130141
我们将针对这一串中第三个数0,第四个数1,第五个数4分别讨论当前位上1出现的次数,这三个数已经包括了上述三种情况。我们将给定数的左边定义为left,右边为right。
对于 第三位数0
当left为0-12时第三位取1,right可以取任意0-999可以保证组合出来的数在合理范围内,但left=13时right没有办法取到1(不然就超了)所以在对于第三位来说1出现的次数即为left*10^(size-i-1)
对于 第四位数1
当left为0-129时第四位取1,right可以取任意0-999可以保证组合出来的数在合理范围内,但left=130时right没有办法取到大于42(不然就超了)所以在对于第四位来说1出现的次数即为left*10^(size-i-1)+right+1;
对于 第五位数4
当left为0-1301时第五位取1,right可以取任意0-999可以保证组合出来的数在合理范围内,所以在对于第五位来说1出现的次数即为(left+1)*10^(size-i-1);
所有的数不会逃出0,1,大于1三种情况因此我们只需要遍历每一位,累加相应的出现1的次数即可。
class Solution {
public:
int numberOf1Between1AndN_Solution(int n) {
if(!n)return 0;
vector<int>v;
int res=0;
while(n){
v.push_back(n%10);
n/=10;
}
reverse(v.begin(),v.end());
for(int i=0;i<v.size();i++){
int left = 0,right=0;
int t =pow(10,v.size()-1-i);
for(int j=0;j<i;j++)left=left*10+v[j];
for(int j=i+1;j<v.size();j++)right=right*10+v[j];
res+=left*t;
//等于1的情况
if(v[i]==1)res+=right+1;
//大于1的情况
else if(v[i]>1)res+=t;
}
return res;
}
};