輸入乙個正整數n,求從1到n這n個整數的十進位制表示中1出現的次數。例如輸入12,從1到12這些整數中包含1的數字有1,10,11和12。1一共出現了5次。
暴力解法就是從頭到尾說每個數中1的個數,然後求和。數每個數中1的個數需要的複雜度為o(logn),而每個數都需要數一遍,因此總的演算法複雜度為o(nlogn)。
遞迴法就是把數按位對數進行拆分,一步一步縮小數的規模,比如 n=23417 時,可以把問題拆為1–3417和3418–23417,每步先對後一部分進行計算,前一部分可以看成 n=3417 時的求解。這樣就可以先分析下 3418–23417 一共有多少個1呢?首先這些數中包含 10000~19999 這一萬個第乙個數字為1的數。就是先算下和後面乙個數字數相同時第乙個數字為1的數一共多少個。後面乙個數去掉第乙個數字後還有4位,這4為每乙個位上都能為1,當其中一位為1時,其它3位可以是 0-9 這10個數中的任何乙個,因此一共的個數字 4∗3
104*3^
4∗31
0,又由於第乙個數字2,因此3418–23417的1的總個數字2∗4
∗3
102*4*3^
2∗4∗31
0,最高位從1到2後面的數重複兩遍。我在做這一題的時候,前期有點疑惑的時,在排列組合後面4位數時,這個方法沒有考慮重複的情況,假如第一位選了1,第二個數從0-9 10個數都選一遍,然後第二個數選了1時,第乙個數又從 0-9 10個數選了一遍,這就出現了兩次兩個都為1的數,是不是重複計算了?後來仔細想想發現並沒有,因為每個位只計算在他的位上出現1的次數,比如剛才說的出現兩個數都是1的情況,這時當每個位選1時只算了一次,但其實這個數有兩個1,因此後面的再算一次才能把1都統計了,這個問題是統計數字1出現的次數,不是每個數的情況,乙個數中存在幾個1都要加進去。
**如下:
def numberof1temp(n):
if 0 < n < 10:
return 1
if n <= 0:
return 0
sum_1 = 0
str_n = str(n)
len_n = len(str_n)
temp = int(str_n[1:])
first_number = int(str_n[0])
numberoffirst = numberof1firstnumber(str_n)
sum_1 += numberoffirst
numberofleft = first_number * (len_n - 1) * 10**(len_n-2)
sum_1 += numberofleft
sum_1 += numberof1temp(temp)
return sum_1
def numberof1firstnumber(str_n):
first_number = int(str_n[0])
if first_number > 1:
number = 10**(len(str_n)-1)
else:
number = int(str_n[1:]) + 1
return number
通過分析,可以發現:
1到10,個位數是1的個數是:1
1到100,十位數是1的個數是:10
1到1000,百位數是1的個數是:100
因此可以通過看這些數包含幾個10,幾個100,幾個1000來計算1出現的次數。還以23417為例,從1到23417要包括多少個10?數數時:1, 2,3, 4, 5, 6, 7, 8, 9,10,然後1,2, 3, 4, 5, 6, 7, 8, 9, 20,再數 1,2, 3, 4, 5, 6, 7, 8, 9, 30。可以看出除了十位數發生了變化,只是相當於個位數重複了一遍。在數數時,大家也經常隻數個位數,從1到10數一遍記一下,這樣方便好數。十位數同理。23417÷10
=2341
23417\div10=2341
23417÷
10=2
341 那個位數上出現的1的個數就是2341+1
2341+1
2341+1
之所以加1是因為從2341-2347,個位還會再出現乙個1。這個思路從數數隻數個位數,數完一遍記下數,比較好理解。在寫**時,有一點需要注意的是,迴圈時數n的值不應該每次變為n//base,而應該改變base為10*base,因為n//base的餘數是需要留下來計算的,餘數中1出現的次數不能忘了。比如在算十位上1出現的次數時23417
÷100
=234
23417\div100=234
23417÷
100=
234,1出現的次數234∗10
234*10
234∗10
,餘數17十位上1出現的次數也要算進去的,234∗10
234*10
234∗10
只算了 1-23400 中十位上1出現的次數,還有 23401-23417 中十位上1出現的次數。
**如下:
def numberof1(n):
sum_1 = 0
base = 10
while n//(base//10):
m = n // base
b = n % base
if b >= 2 * base//10:
b_sum = base//10
elif b >= 1 * base//10:
b_sum = b % (base//10) + 1
else:
b_sum = 0
sum_1 += m * base//10 + b_sum
base *= 10
return sum_1
在理解演算法時,需要記得計算的是所有數中1出現的次數,也就是後面兩個演算法中分別考慮每個位上1出現的次數。 整數中1出現的次數(從1到n整數中1出現的次數)
求出1 13的整數中1出現的次數,並算出100 1300的整數中1出現的次數?為此他特別數了一下1 13中包含1的數字有1 10 11 12 13因此共出現6次,但是對於後面問題他就沒轍了。acmer希望你們幫幫他,並把問題更加普遍化,可以很快的求出任意非負整數區間中1出現的次數。include u...
整數中1出現的次數(從1到n整數中1出現的次數)
時間限制 1秒 空間限制 32768k 題目描述 include using namespace std class solution 求之前的length 1位中含乙個數 int base1 0 int base2 1 for int i 0 i1 i cout cout cout 求從base2...
整數中1出現的次數(從1到n整數中1出現的次數)
求出1 13的整數中1出現的次數,並算出100 1300的整數中1出現的次數?為此他特別數了一下1 13中包含1的數字有1 10 11 12 13因此共出現6次,但是對於後面問題他就沒轍了。acmer希望你們幫幫他,並把問題更加普遍化,可以很快的求出任意非負整數區間中1出現的次數。演算法一 暴力累加...