題目描述
求出1~13
的整數中1出現的次數,並算出100~1300
的整數中1出現的次數?爲此他特別數了一下1~13
中包含1的數字有1、10、11、12、13因此共出現6次,但是對於後面問題他就沒轍了。ACMer希望你們幫幫他,並把問題更加普遍化,可以很快的求出任意非負整數區間中1出現的次數(從1 到 n 中1出現的次數)。
分析
- 方法一:記錄單個數字所包含1的個數,相加即可。時間複雜度>O(n),空間複雜度爲O(n)。
- 方法二:歸納:
- 從 1 至 10,在它們的個位數中,任意的 X 都出現了 1 次。
- 從 1 至 100,在它們的十位數中,任意的 X 都出現了 10 次。
- 從 1 至 1000,在它們的百位數中,任意的 X 都出現了 100 次。
依此類推,從 1 至 ,在它們的左數第二位(右數第 i位)中,任意的 X 都出現了 次。
-
總結一下以上的算法,可以看到,當計算右數第 i 位包含的 1 的個數時:
- 取第 i 位左邊(高位)的數字,乘以 ,得到基礎值 a。
- 取第 i 位數字,計算修正值:
- 如果大於 1,則結果爲 a+ 。
- 如果小於 1,則結果爲 a。
- 如果等於 1,則取第 i 位右邊(低位)數字,設爲 b,最後結果爲 a+b+1。
-
時間複雜度
O(n的位數)
,空間複雜度O(n)
。
舉例說明
:
接下來以 n=2593,X=5n=2593,X=5 爲例來解釋如何得到數學公式。從 1 至 2593 中,數字 5 總計出現了 813 次,其中有 259 次出現在個位,260 次出現在十位,294 次出現在百位,0 次出現在千位。
現在依次分析這些數據,首先是個位。從 1 至 2590 中,包含了 259 個 10,因此任意的 X 都出現了 259 次。最後剩餘的三個數 2591, 2592 和 2593,因爲它們最大的個位數字 3 < X,因此不會包含任何 5。
然後是十位。從 1 至 2500 中,包含了 25 個 100,因此任意的 X 都出現了 25×10=25025×10=250 次。剩下的數字是從 2501 至 2593,它們最大的十位數字 9 > X,因此會包含全部 10 個 5。最後總計 250 + 10 = 260。
接下來是百位。從 1 至 2000 中,包含了 2 個 1000,因此任意的 X 都出現了 2×100=2002×100=200 次。剩下的數字是從 2001 至 2593,它們最大的百位數字 5 == X,這時情況就略微複雜,它們的百位肯定是包含 5 的,但不會包含全部 100 個。如果把百位是 5 的數字列出來,是從 2500 至 2593,數字的個數與百位和十位數字相關,是 93+1 = 94。最後總計 200 + 94 = 294。
最後是千位。現在已經沒有更高位,因此直接看最大的千位數字 2 < X,所以不會包含任何 5。到此爲止,已經計算出全部數字 5 的出現次數。
代碼
- 方法一:
# -*- coding:utf-8 -*-
class Solution:
def NumberOf1Between1AndN_Solution(self, n):
# write code here
counter = 0
for i in range(n+1):
counter += self.has1Counter(i)
return counter
def has1Counter(self, number):
counter = 0
while number:
if number%10==1:
counter += 1
number //= 10
return counter
- 方法二:
# -*- coding:utf-8 -*-
class Solution:
def NumberOf1Between1AndN_Solution(self, n):
# write code here
counter = 0
for i in range(1,len(str(n))+1):
high = n//(10**i) # 高位數字
low = n%(10**(i-1)) # 低位數字
now = n//10**(i-1)%10 # 當前位
if now<1:
counter += high * 10**(i-1)
elif now>1:
counter += high *10**(i-1) + 10**(i-1)
else:
counter += high*10**(i-1) + low + 1
return counter