18.4 Write a method to count the number of 2s between 0 and n.
這道題給了我們一個整數n,讓我們求[0,n]區間內所有2出現的個數,比如如果n=20,那麼滿足題意的是2, 12, 20,那麼返回3即可。LeetCode上有一道很類似的題Factorial Trailing Zeroes,但是那道題求5的個數還包括了因子中的5,比如10裡面也有5,這是兩題的不同之處。那麼首先這題可以用brute force來解,我們對區間內的每一個數字都呼叫一個函式,用來統計該數字中2出現的個數。而統計一個數字各位上而出現的個數很簡單,就是平移位,對10取餘,如果為2,則計數器自增1,然後此數自除以10,直至為0停止,參見程式碼如下:
解法一:
int count_number_2(int num) {
int res = ;
while (num > ) {
if (num % == ) ++res;
num /= ;
}
return res;
} int count_in_range(int num) {
int res = ;
for (int i = ; i <= num; ++i) {
res += count_number_2(i);
}
return res;
}
其實這道題還有更好的辦法,我們不是在區間裡一個數一個數的找2,而是按位來找,比如我們先來列出一部分序列:
0 1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29
...
110 111 112 113 114 115 116 117 118 119
我們發現最低位出現2的頻率是每10個數字出現1個,我們大概可以分為三種情況來討論,digit < 2, digti = 2, 和digit > 2。
當digit < 2時,例如x=61523, d=3, 那麼x[d]=1,從低位開始座標為3的數字是1(從0開始),那麼第三位上的2出現於2000-2999, 12000-12999, 22000-22999, 32000-32999, 42000-42999, 52000-52999, 所以總共有6000個2,在第三位上。怎麼跟我們在區間[1,60000]上只計算第三位上的2的個數相同。
當digit > 2時,大於2的情況跟上面的分析方法相同,比如x=63523,那麼這跟區間[0,70000]上第三位是2的個數相同,所以我們rounding up一下即可。
當digit = 2時,比如x=62523,我們知道[0,61999]區間的第三位2的個數可以通過上面第一種情況計算出來,那麼怎麼計算[62000,62523]區間中第三位2的個數呢,共有524個,用62523-62000+1即可。
根據上述分析,我們不難寫出程式碼如下:
解法二:
int count_in_range_as_digit(int num, int d) {
int power_of_10 = pow(, d);
int next_power_of_10 = power_of_10 * ;
int right = num % power_of_10;
int round_down = num - num % next_power_of_10;
int round_up = round_down + next_power_of_10;
int digit = (num / power_of_10) % ;
if (digit < ) return round_down / ;
else if (digit == ) return round_down / + right + ;
else return round_up / ;
} int count_in_range(int num) {
int res = ;
int len = to_string(num).size();
for (int i = ; i < len; ++i) {
res += count_in_range_as_digit(num, i);
}
return res;
}