1.题目
求出1~13
的整数中1出现的次数,并算出100~1300
的整数中1
出现的次数?为此他特别数了一下1~13
中包含1的数字有1、10、11、12、13
因此共出现6
次,但是对于后面问题他就没辙了。ACMer
希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1
出现的次数(从1
到n
中1
出现的次数)。
2.我的题解
笨方法,一个数一个数累加,一个数中一位一位累加。
class Solution {
int numOf1(int n) {
int res = 0;
while (n) {
if (n % 10 == 1)res++;
n /= 10;
}
return res;
}
public:
int NumberOf1Between1AndN_Solution(int n)
{
int res = 0;
for (int i = 1; i <= n; i++) {
res += numOf1(i);
}
return res;
}
};
3.别人的题解
3.1 硬数版
我的代码太冗长了。
链接:https://www.nowcoder.com/questionTerminal/bd7f978302044eee894445e244c7eee6?answerType=1&f=discussion
来源:牛客网
public int NumberOf1Between1AndN_Solution(int n) {
int count=0;
for(int i=n;i>0;i--){
for(int j=i;j>0;j/=10){
if(j%10==1) count++;
}
}
return count;
}
3.2 数学归纳法公式版
链接:https://www.nowcoder.com/questionTerminal/bd7f978302044eee894445e244c7eee6?answerType=1&f=discussion
来源:牛客网
思路是分别计算个位、十位、百位…上出现 1
的个数。
以 n =216
为例:
- 个位上:
1 ,11,21,31,.....211
。个位上共出现(216/10)+ 1
个1
。因为除法取整,210~216
间个位上的1
取不到,所以我们加8
进位。 - 你可能说为什么不加
9
,这里把最后取到的个位数为1
的单独考虑。当前位上需要特殊考虑的取1
的数量与当前位的数值、更低位的那些数有关。比如111
和112
在计算个位数上为1
的情况时,112+8
进1
了取12
,111+8
虽然没进1
,但是由于有末位数为1
的特殊情况,总数也加了1
,因此+8
的操作正好。 - 十位上:
10~19,110~119,210~216
. 十位上可看成 求(216/10)=21
个位上的1
的个数然后乘10
。这里再次把最后取到的十位数为1
的单独拿出来,即210~216
要单独考虑 ,个数为(216%10)+1
。 - 后面以此类推。
- 时间复杂度 :
O(logn)
public int NumberOf1Between1AndN_Solution(int n) {
int cnt = 0;
for (int m = 1; m <= n; m *= 10) {
int a = n / m, b = n % m;
cnt += (a + 8) / 10 * m + (a % 10 == 1 ? b + 1 : 0);
}
return cnt;
}