劍指offer(43):詳解1 ~n 整數中 1 出現的次數

本博客主要內容爲圖書《劍指offer》43 題的解題思路及代碼。方法可能還有不足之處,歡迎大家討論評論。

1. 題目描述

  輸入一個整數 n ,求出 1~n 這 n 個整數的十進制表示中 1 出現的次數。例如輸入 12,1 ~ 12 這些整數中包含 1 的數字有 1 ,10,11,12,“1 ”一種出現了 5 次。(注意其中 11 中 1 出現了兩次)

2. 解題思路

2.1 最高位爲 1 的情況

  對於某一個數字,如 1234 ,考慮 1~1234 中 1 一共出現了多少次的時候,可以進行如下分解:

1:1234=1:999+1000:1234

這個時候只需要分 1 : 999 和 1000 : 1234 兩步進行考慮就行。

  對於第一部分,我們可以觀察到一個比較強的規律。當數字是一位數的時候,數字的長度 length=1 ,也就是說 1~9 只包含 1 個 1,即

f(length=1)=1

當數字是二位數的時候,數字的長度 length=2 ,我們可以將 1~99在分解爲三個部分,即
1:99=1:9+10:19+2099

其中 1~9 的結果爲f(length)=1 , 10~19 一共有十個數(二十個數字),其中十位都是1 ,有 10 個1;剩下 10個個位的情況是 0~9 中有多少個 1 ,這個情況與 1~9 的結果是相同的;20~99有 (92+1) 個1,即
f(length=2)=1+(10+1)+(92+1)

通過進行歸納可以得到下面的通式
f(length)=10n1+10f(length1)

其中的 length 是對應數字的長度。

  對於第二部分,即 1000:1234 ,其中最高位重複了 12341000+1 次,即n10length1+1 ,然後考慮除了最高位剩下位(0~234)中 1 出現的次數,即計算g(n10length1)

  所以數字最高位爲 1 的情況的計算表達式如下:

g(n)=f(length1)+n10length1+1+g(n10length1)

其中的f(length)=10n1+10f(length1) ,n 是輸入的數字,length 是輸入數字的長度。

2.2 最高位>1 的情況

  對於某一個數字,如 4234 ,考慮 1~4234 中 1 一共出現了多少次的時候,可以分解如下的四個部分:

1:4234=1:999+1000:1999+2000:3999+4000:4234

其中 1:999 可以通過 f 計算得到;1000:1999 中最高位出現的次數爲 10length1 ,而剩下的位數出現 1 的總次數爲 f(length1) ;2000:3999中最高位不會出現 1 ,剩下的位出現 1 的問題又變成了 1:999,即 (32+1)f(length1) ,最後剩下 4000:4234 ,而這個問題等價爲 0:234 中有多少個 1 的問題,可以通過 g(na10length1) 來解決。所以此時的通式爲
g(n)=10length1+af(length1)+g(na10length1)

其中的 a 是最高位的數字

2.3 情況總結

  所以計算這個問題的遞推公式如下:

g(n)={f(length1)+n10length1+1+g(n10length1),if 最高位爲110length1+af(length1)+g(na10length1),if 最高位非1

其中
f(length)=10n1+10f(length1)

所以總體的思想就是,根據輸入數字的最高位對情況進行分類,之後帶入上面的遞推公式,進行遞歸操作。遞歸的停止條件爲剩下的數字的長度爲 1,如果剩下的數字是0返回0,否則返回1.

3. 代碼實現

# -*- coding:utf-8 -*-
class Solution:
    def NumberOf1Between1AndN_Solution(self, n):
        # 如果數字爲 0 則返回 0
        if not n:
            return 0

        # 將數字轉化爲字符串,得到數字的長度
        strN = str(n)
        length = len(strN)

        # 通過 f 函數計算遞推通式中的 f (length -1)
        mydicdictionary = self.dictionary(length-1)

        #如果長度爲 1 且不爲 0 話就返回 1 
        if length == 1:
            return 1

        # 求出當前數字的最高位數字
        temp = 10**(length-1)
        a = n//temp

        # 根據判斷結果執行遞推公式
        if n-temp >= temp:
            return temp + a*mydicdictionary + self.NumberOf1Between1AndN_Solution( n-a*temp)
        else:
            return mydicdictionary + n-temp + 1 +self.NumberOf1Between1AndN_Solution( n-temp)

    def dictionary(self,length):
        mydicdictionary = [0]
        for i in range(1 , length+1):
            result = 10**(i-1) + 10*mydicdictionary[i-1]
            mydicdictionary.append(result)
        return mydicdictionary.pop()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章