题意
给定一个正整数 n n n,计算 [ 1 , n ] [1,n] [1,n]中所有整数出现的 1 1 1的个数之和。
题解
设 n n n的数字长度为 w w w, n = d w d w − 1 . . . d 1 . n=d_wd_{w-1}...d_1. n=dwdw−1...d1.考虑依次对这 w w w位进行计数。若现在正考虑从右向左第 i i i位: v = c w c w − 1 . . . c i + 1 1 c i − 1 . . . 1. v=c_wc_{w-1}...c_{i+1}1c_{i-1}...1. v=cwcw−1...ci+11ci−1...1.设左边的 w − i w-i w−i位 ( c w . . . c i + 1 ) (c_w...c_{i+1}) (cw...ci+1)为 x x x,右边的 i − 1 i-1 i−1位 ( c i − 1 . . . c 1 ) (c_{i-1}...c_1) (ci−1...c1)为 y . y. y.
(
1
)
(1)
(1)当
x
<
d
w
.
.
.
d
i
+
1
x<d_w...d_{i+1}
x<dw...di+1时,这时无论
y
y
y是什么值
v
v
v都不会大于
n
n
n.因此此时
x
x
x有
[
0
,
d
w
−
1
]
[0,d_w-1]
[0,dw−1]共
d
w
d_w
dw种选法,
y
y
y有
[
00...0
,
99...9
]
[00...0,99...9]
[00...0,99...9]共
1
0
l
e
n
(
y
)
−
1
10^{len(y)-1}
10len(y)−1种选法。举个例子:当
n
=
2022
n=2022
n=2022时,要统计
i
=
2
i=2
i=2时(即十位为1的贡献),
c
3
c
2
1
c
1
c_3c_21c_1
c3c21c1中的
x
=
c
3
c
2
<
20
x=c_3c_2<20
x=c3c2<20时(共
20
20
20种选法),
c
1
c_1
c1都有
[
0
,
9
]
[0,9]
[0,9]共
10
10
10种选法。因此
i
=
2
i=2
i=2的贡献为
20
∗
10
=
200
20*10=200
20∗10=200。
(
2
)
(2)
(2)当
x
=
d
w
.
.
.
d
i
+
1
x=d_w...d_{i+1}
x=dw...di+1时,这时
y
y
y不能超过
d
i
−
1
.
.
.
d
1
.
d_{i-1}...d_1.
di−1...d1.因此此时贡献为
x
x
x固定,
y
y
y取
[
00...0
,
d
i
−
1
.
.
.
d
1
]
[00...0,d_{i-1}...d_1]
[00...0,di−1...d1]的方案数,即
(
d
i
−
1
.
.
.
d
1
)
+
1.
(d_{i-1}...d_1)+1.
(di−1...d1)+1.上面的例子中此情况的贡献为
9
+
1
=
10
9+1=10
9+1=10,因此
i
=
2
i=2
i=2时的总贡献为
(
1
)
+
(
2
)
=
200
+
10
=
210.
(1)+(2)=200+10=210.
(1)+(2)=200+10=210.
上面考虑的情况为
1
<
i
<
w
1<i<w
1<i<w时的情况,当
i
=
1
i=1
i=1和
i
=
w
i=w
i=w时,情况类似:
(
a
)
(a)
(a)当
i
=
1
i=1
i=1时,仍有
(
1
)
(1)
(1)情况中的贡献;仍需要判断当
d
1
≥
1
d_1\ge1
d1≥1时,
c
w
c
w
−
1
.
.
.
c
2
1
c_wc_{w-1}...c_21
cwcw−1...c21也在
[
1
,
n
]
[1,n]
[1,n]中,答案再加
1.
1.
1.
(
b
)
(b)
(b)当
i
=
w
i=w
i=w时,当
d
w
=
1
d_w=1
dw=1时(可以保证
d
w
≠
0
d_w\ne0
dw=0),
y
y
y可以取到
[
00...0
,
d
i
−
1
.
.
.
d
1
]
[00...0,d_{i-1}...d_1]
[00...0,di−1...d1]中的任何值,贡献为
(
d
i
−
1
.
.
.
d
1
)
+
1.
(d_{i-1}...d_1)+1.
(di−1...d1)+1.否则
d
w
≠
1
,
d_w\neq1,
dw=1,此时没有限制,贡献为
1
0
w
−
1
10^{w-1}
10w−1.
代码
class Solution {
public:
int countDigitOne(int n) {
int sum = n;
int len = ceil(log10(n + 1));//即上面提到的w
//left=d[w]...d[i+1],right=d[i-1]...d[1]
int left = n / 10;
int right = 0;
int ten[] = {1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000};//存10的幂
char buf[11];
int ptr = 0;
//先用sum把n存到数组中
while(sum) {
buf[++ptr] = (sum%10) + '0';
sum /= 10;
}
for(int i = 1; i <= len; i++) {
if(i == 1) {
sum += left * ten[i - 1];
if(buf[i] >= '1') sum++;
}
else if(i == len) {
if(buf[i] == '1') sum += right + 1;
else sum += ten[i - 1];
}
else {
sum += left * ten[i - 1];
if('1' < buf[i]) sum += ten[i - 1];
else if('1' == buf[i]) sum += right + 1;
}
//更新left和right
left /= 10;
right += (buf[i] - '0') * ten[i - 1];
}
return sum;
}
};