1. 程式人生 > >(lintcode)第3題統計數字

(lintcode)第3題統計數字

要求:計算數字k在0到n中的出現的次數,k可能是0~9的一個值。

樣例
例如n=12,k=1,在 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],我們發現1出現了5次 (1, 10, 11, 12)

首先我們想到暴力破解法,也就是遍歷每一個數,同時每一個數都遍歷每一位,遍歷每一位的方法是不斷地整除10,直到整除為0.程式碼如下:

class Solution {
    /*
     * param k : As description.
     * param n : As description.
     * return: An integer denote the count of digit k in 1..n
     */
   public int digitCounts(int k, int n) {
        // write your code here
        int sum=0;
        for(int i=0;i<=n;i++){
            int num=i;
            while(num/10!=0){
                if(num%10==k){
                    sum++;
                }
                num=num/10;
            }
            if(num==k)
            sum++;
        }
        
        return sum;
    }
}
還有另一種方法,來自於《程式設計之美》,提高了效率。

思路如下:假如我們現在要求從1到一個5位數ABCDE之間,出現了2的數次是多少。我們現在把問題簡單化,只考慮從0到ABCDE之間在百位(C)上出現2的次數,然後用在個位,十位,千位,萬位上也適用,就可以算出總的。

第一種情況:當給出的五位數ABCDE中百位上的C是小於數字2的時候:

1.比如說,百位上是0,假如給定的五位數是12031,那麼0到12031之間有哪一些數的百位上會出現2,從小到大數,200-299,1200-1299,2200-2299,3200-3299...,10200-10299,11200-11299.我們會發現後面三位是固定的,變化的只有比百位更高的位,高位從0(0代表200-299)到11,總共12個,再變大就超過了,因為百位上的數字是小於給定數字2的。那麼我們發現當百位上是0的時候,百位上出現2的次數由更高的位決定,等於更高的位(12)X當前的位數100=1200個。

2.當百位上是1的時候,假設給定的五位數是12131,那麼求出來的結果和百位上是0的一樣,所以從1到ABCDE所有數字中,百位上出現2的次數也是1200.

第二種情況,也就是當給定的五位數ABCDE百位上的C剛好等我我們要求的次數的那個數字2

假如現在給定了一個12213,那麼我們還是有200-299,1200-1299,2200-2299,3200-3299...,10200-10299,11200-11299這1200個,但是除了這些,還有一部分12200-12213(由低位數字+1),所以,當某一位的數字等於2的時候,百位上出現2的次數=更高位的數字x當前的位數+低位的數字+1;

第三種情況,當百位上的數字大於2的時候

假如說12313,那麼這次除了200-299,1200-1299,2200-2299,3200-3299...,10200-10299,11200-11299,還包含了12200-12299,也沒有低位的事情計算百位上出現2的次數是:(更高的位數+1)x當前的位數。

總結以下規律,我們要從1到ABCDE中找到k這個數字在某一位上出現了多少次

 當某一位的數字小於k時,那麼該位出現i的次數為:更高位數字x當前位數 
當某一位的數字等於k時,那麼該位出現i的次數為:更高位數字x當前位數+低位數字+1 
 當某一位的數字大於k時,那麼該位出現i的次數為:(更高位數字+1)x當前位數

程式碼如下:

class Solution {
  /*
   * param k : As description.
   * param n : As description.
   * return: An integer denote the count of digit k in 1..n
   */
  public int digitCounts(int k, int n) {
    if(k == 0 && n<10)
      return 1; // 特殊情況
    int temp = n, cnt = 0, pow = 1;//pow代表當前位的後面低位是多少,1為個位,10為十位,100位千位
    while(temp != 0) {
      int digit = temp % 10; // 根據當前位置數和k的大小關係,可以算出當前位置出現過k的次數
      if(digit < k)
        cnt += (temp / 10) * pow;
      else if(digit == k)
        cnt += (temp / 10) * pow + (n - temp * pow + 1);
      else {
        if(!(k == 0 && temp / 10 == 0)) // 排除沒有更高位時,尋找的數為0的情況
          cnt += (temp / 10 + 1) * pow;
      }
      temp /= 10;
      pow *= 10;
    }
    return cnt;
  }
};