題目:輸入一個整數 n,求 1~n 這 n 個整數的十進制表示中 1 出現的次數。例如,輸入 12,1~12 這些整數中包含 1 的數字有 1、10、11 和 12,1 一共出現了 5 此。
對於 X ∈ [1, 9]
,1~n 整數中 X 出現的次數在算法一樣。(0 的比較特殊,不過大致思想類似)。
解題思路:
分別以 12493、12593、12693 爲例,計算數字 5 出現的次數。
其中,結果爲 5 分別在個位、十位,直到萬位出現的次數的和。
以 5 在百位上出現的次數爲例進行講解:
-
對於 12493,百位上位爲 4 < 5,所以其更高位,即萬、千位只能從 00 - 11 進行變換,即
01、02...10、11
進行變化,共 12 種;而其更低位,即十、個位只能從 00 - 99 進行變換(因爲萬千位最大也才 11,即 115XX,此時十個位最大可爲 99,即 11599,小於 12493),共 100 種。因此,對於 12493,5 在百位出現的次數位 12 * 100 = 1200
-
對於 12593,百位上爲 5 == 5。所以其更高位,即萬、千位只能從 00 - 12 進行變換;而其更低位,就需要看情況了:
(1)當萬千位爲 00 - 11 的時候,百十位可以是 00 - 99 進行變換;
(2)當萬千位爲 12 的時候,因爲不能大過 12593,因此只能從 00 - 93 進行變換,共 94 種。因此,對於 12493,5 在百位出現的次數位 12 * 100 + 94 = 1294
-
對於 12693,百位上爲 6 > 5。所以其更高位,即萬、千位能從 00 - 12 進行變換,共 13 種;而其更低位,即十、個位能從 00 - 99 進行變換(因爲萬千位最大也才 12,即 125XX,此時十個位最大可爲 99,即 12599,小於 12693),共 100 種。
因此,對於 12693,5 在百位出現的次數位 13 * 100 = 1300
對於 5 在其他位上出現的次數,也類似。
算法的具體實現:
// 用於控制數字 X 出現的次數
private static int X = 1;
public int NumberOf1Between1AndN_Solution(int n) {
if (n < 1) return 0;
if (n < 10) return 1;
int sum = 0;
// 用於輔助判斷是不是把 n 的每一位遍歷完了
int tag = n;
// 用於記錄位的索引,從 n 的個位向高位開始
int index = 1;
while (tag > 0) {
int mi = (int) Math.pow(10, index);
int num = n % mi;
// 得到當前位的更高位
int high = n / mi;
int mi2 = (mi / 10);
// 得到當前位
int cur = num / mi2;
// 得到當前位的更地位
int low = num % mi2;
// 記錄當前位出現 X 的次數
int count = 0;
if (cur < X) {
count = high * mi2;
} else if (cur > X) {
count = (high + 1) * mi2;
} else {
count = high * mi2 + (low + 1);
}
tag /= 10;
++index;
System.out.println(count);
sum += count;
}
return sum;
}
一個數 n 有 ((O(log10 n) 取整) + 1) 位,因此上述算法的時間複雜度爲 O(log10 n )。
爲什麼數 n 有 ((O(log10 n) 取整) + 1) 位?
因爲假設 n ∈ [10x, 10x+1-1],且 n 爲整數,則 n 對於的位數屬於 x + 1。例如 199,其屬於 [100, 999],有 log10 100 = 2,則 199 對應的位數爲 2 + 1 = 3.