統計從1到n中1出現的次數?

Leetcode NUM1
統計從1到n中1出現的次數?

解題思路一(代碼簡單思路較難想到):

通過使用一個位置乘子m 遍歷數字的位置, m 分別爲1,10,100,1000…etc.(m<=n)
對於每個位置來說,把10進制數分成兩個部分,比如說 當m=100的時候, 把十進制數 n=3141592 分成 a=31415 和 b=92 ,以此來分析百位數爲1時所有數的個數和。m=100時,百位數的前綴爲3141,當百位數大於1時,爲3142100,因爲當百位數大於1時,前綴可以爲0,即百位數可以從100到199,共100個數;當百位數不大於1時,爲3141100;如何判斷百位數是否大於1?假設百位數爲x,若(x+8)/10等於1,則大於1,若(x+8)/10等於0,則小於1。因此前綴可用(n/m + 8)/10 m來計算(若計算2的個數,可以改爲(n/m + 7)/10m,若計算3的個數,改爲(n/m + 6)/10*m,…以此類推)。

再例如m=1000時,n分爲a=3141和 b=592;千位數的前綴爲314,千位數不大於1,故前綴計算爲3141000;因爲千位數爲1,再加b+1(0到592)。即千位數爲1的所有書的個數和爲3141000+592+1;公式(n/m + 8)/10*m + b +1。

注意:只有n的第m位爲1時需要計算後綴,後綴計算爲 (n/m%10==1)*(b+1),

即(n/m%101)判斷第m位是否爲1,若爲1,則加上(b+1),若不爲1,則只計算前綴。(若計算2的個數,可以改爲(n/m%102)(b+1),若計算3的個數,可以改爲(n/m%10==3)(b+1)…以此類推)
代碼:

int NumberOf1Between1AndN_Solution(int n)
    {
        int cnt = 0;
        int a=0,b=0;
        for(long long m=1;m<=n;m*=10){
            a = n/m;
            b = n%m;
            cnt+=(a+8)/10*m + (a%10==1)*(b+1);
        }
        return cnt;
    }

解題思路2:

出現1無非是在個位、十位、百位、千位。。。等出現,那對一個數判斷是否有1便是判斷他的各個位有沒有1出現。

例子:
(1)個位上出現1的數有什麼規律呢,個位出現1的數可寫作x10+1, 則有 ((x10+1)-1)%10<1.
(2)十位上出現1的數又有什麼規律呢,十位出現1的數可寫作 x100+10+y,(y<10),則有 ((x100+10+y)-10)%100=y<10.
(3)再來,百位出現1的數字有什麼規律呢,百位出現1的數可寫作x1000+100+y,y<100,則有((x1000+100+y)-100)%1000=y<100
總結:舉例子只是爲了方便理解,統一的來說,當10的k次方出現的1的數都可以寫作,number=x*10(k+1)+10k+y,其中y<10k,則恆有(number-10k)%10(k+1)<=y<10k
方法:對一個數是否滿足條件來說,先求出該數的位數length,然後遍歷k=1->length,判斷是否有滿足上式的k值存在,若存在,則該數符合條件。
代碼:

class Solution {
public:
    int NumberOf1Between1AndN_Solution(int n)
    {
        int res = 0, length;
        for (int i = 1; i <= n; i++) {
            length = 0;
            while (i >= pow(10, length)) {
                if (((i -(int) pow(10, length)) % (int)pow(10, length + 1))<pow(10, length))
                    res++;
                length++;
           }
        }
        return res;
    }
};

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章