1.题目描述
小明想求出1-13的整数中1出现的次数,或者算出100-1300的整数中1出现的次数。为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。小明希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。
2.算法描述
方法1(投机):
遍历1-n所有数字,将数字转换为字符数组。统计字符数组中1的个数。得到答案。
方法2(推荐):
分别去看个、十、百、千、万等等位置上分别出现1的次数,最后相加得到总次数。
难点是,
如
何
找
到
某
一
个
数
位
上
1
出
现
的
次
数
呢
?
\red{如何找到某一个数位上1出现的次数呢?}
如何找到某一个数位上1出现的次数呢?
下面以123x456为例子,来讲解123x456中千位1出现的次数。其他位上的1与此是类似的。
1.首先将123x456分为两个部分:
a
=
123
x
456
/
1000
=
123
x
a = 123x456/1000=123x
a=123x456/1000=123x
b
=
123
x
456
%
1000
=
456
b = 123x456\%1000 = 456
b=123x456%1000=456
1)如果 x > = 2 \red{x>=2} x>=2
则
x
=
1
x=1
x=1出现的次数有
(
a
/
10
+
1
)
=
123
+
1
=
124
(a/10+1)=123+1=124
(a/10+1)=123+1=124, 即千位以上的数可以是0-123中的任何一个,在当中任取一个作为千位以上的数,1都会出现
1000
1000
1000个,哪
1000
个
1
呢
?
_
1000
−
_
1999
。
其
中
_
可
以
填
0
−
123
中
的
任
何
一
个
\red{1000个1呢?\_1000-\_1999。其中\_可以填0-123中的任何一个}
1000个1呢?_1000−_1999。其中_可以填0−123中的任何一个。
故:
x
>
=
2
x>=2
x>=2时,千位上出现1的总次数
=
(
a
/
10
+
1
)
∗
1000
=(a/10+1)*1000
=(a/10+1)∗1000
2)如果 x = 1 \red{x=1} x=1
则
x
=
1
x=1
x=1出现的次数有
(
a
/
10
+
1
)
=
123
+
1
=
124
(a/10+1)=123+1=124
(a/10+1)=123+1=124, 即千位以上的数可以是0-123中的任何一个,在当中任取一个作为千位以上的数,1都会出现
1000
1000
1000个,
但
是
,
当
取
123
时
,
1
不
会
出
现
1000
次
,
因
为
1231456
已
经
到
了
最
大
的
数
字
了
,
而
是
只
会
出
现
(
456
+
1
)
个
1
,
即
_
1000
−
_
1456
,
其
中
_
为
123
\red{但是,当取123时,1不会出现1000次,因为1231456已经到了最大的数字了,而是只会出现(456+1)个1,即\_1000-\_1456,其中\_为123}
但是,当取123时,1不会出现1000次,因为1231456已经到了最大的数字了,而是只会出现(456+1)个1,即_1000−_1456,其中_为123。
故:
x
=
1
x=1
x=1时,千位上出现1的总次数
=
(
a
/
10
)
∗
1000
+
(
b
+
1
)
=(a/10)*1000 + (b+1)
=(a/10)∗1000+(b+1)
3)如果 x = 0 \red{x=0} x=0
则 x = 1 x=1 x=1出现的次数有 a / 10 = 123 a/10=123 a/10=123,即千位以上的数可以是0-122中的任何一个,在当中任取一个作为千位以上的数,1都会出现 1000 1000 1000个,哪 1000 个 1 呢 ? _ 1000 − _ 1999 。 其 中 _ 可 以 填 0 − 122 中 的 任 何 一 个 , 当 _ 为 123 时 , x 只 能 是 0 了 \red{1000个1呢?\_1000-\_1999。其中\_可以填0-122中的任何一个,当\_为123时,x只能是0了} 1000个1呢?_1000−_1999。其中_可以填0−122中的任何一个,当_为123时,x只能是0了。
综上,我们可以得出以下的一半性结论:
初始化ans=0;
将base依次取
1
,
10
,
100
,
1000
,
10000...
1,10,100,1000,10000...
1,10,100,1000,10000...,按照:
a
n
s
=
a
n
s
+
(
a
+
8
)
/
10
∗
b
a
s
e
+
(
a
%
10
=
=
1
?
(
b
+
1
)
:
0
)
ans =ans+(a+8)/10 * base+(a\%10==1?(b+1):0)
ans=ans+(a+8)/10∗base+(a%10==1?(b+1):0),将出现的1,依次加到最终答案中。遍历完成就得到了最终1出现的次数。
其中(a+8)刚好可以处理以上的三种情况,只有在base位上是
>
=
2
>=2
>=2的时候才会进位,而base位上为1或0,则不会进位。
3.代码描述
3.1.Java代码
//方法2
public class Solution {
public int NumberOf1Between1AndN_Solution(int n) {
int ans = 0;
for(int base = 1; base<=n; base*=10){
int a = n /base;
int b = n % base;
ans += (a+8)/10 * base + (a%10==1?(b+1):0);
}
return ans;
}
}
//方法1(不推荐)
public class Solution {
public int NumberOf1Between1AndN_Solution(int n) {
int ans = 0;
for(int i=1; i<=n; i++){
char[] chars = (i+"").toCharArray();
for(char c: chars)
if(c == '1')
ans++;
}
return ans;
}
}
3.2.Python代码
#方法2
# -*- coding:utf-8 -*-
class Solution:
def NumberOf1Between1AndN_Solution(self, n):
# write code here
ans = 0
base = 1
while base<=n:
a, b = n/base, n%base
ans += (a+8)/10*base + (a%10==1)*(b+1)
base *=10
return ans
#方法1(不推荐)
# -*- coding:utf-8 -*-
class Solution:
def NumberOf1Between1AndN_Solution(self, n):
# write code here
ans = 0
for i in range(1,n+1):
s = str(i)
for c in s:
if c == '1':
ans += 1
return ans