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=123x456/1000=123xa = 123x456/1000=123xa=123x456/1000=123x
b=123x456%1000=456b = 123x456\%1000 = 456b=123x456%1000=456
1)如果x>=2\red{x>=2}x>=2
则x=1x=1x=1出现的次数有 (a/10+1)=123+1=124(a/10+1)=123+1=124(a/10+1)=123+1=124, 即千位以上的数可以是0-123中的任何一个,在当中任取一个作为千位以上的数,1都会出现100010001000个,哪1000个1呢?_1000−_1999。其中_可以填0−123中的任何一个\red{1000个1呢?\_1000-\_1999。其中\_可以填0-123中的任何一个}1000个1呢?_1000−_1999。其中_可以填0−123中的任何一个。
故:x>=2x>=2x>=2时,千位上出现1的总次数 =(a/10+1)∗1000=(a/10+1)*1000=(a/10+1)∗1000
2)如果x=1\red{x=1}x=1
则x=1x=1x=1出现的次数有 (a/10+1)=123+1=124(a/10+1)=123+1=124(a/10+1)=123+1=124, 即千位以上的数可以是0-123中的任何一个,在当中任取一个作为千位以上的数,1都会出现100010001000个,但是,当取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=1x=1x=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=1x=1x=1出现的次数有a/10=123a/10=123a/10=123,即千位以上的数可以是0-122中的任何一个,在当中任取一个作为千位以上的数,1都会出现100010001000个,哪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...,按照:ans=ans+(a+8)/10∗base+(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