題目描述: java
親們!!咱們的外國友人YZ這幾天老是睡很差,初中奧數裏有一個題目一直困擾着他,特此他向JOBDU發來求助信,但願親們能幫幫他。問題是:求出1~13的整數中1出現的次數,並算出100~1300的整數中1出現的次數?爲此他特別數了一下1~13中包含1的數字有1、10、11、12、13所以共出現6次,可是對於後面問題他就沒轍了。ACMer但願大家幫幫他,並把問題更加廣泛化,能夠很快的求出任意非負整數區間中1出現的次數。 算法
輸入:輸入有多組數據,每組測試數據爲一行。 編程
每一行有兩個整數a,b(0<=a,b<=1,000,000,000)。 測試
輸出:對應每一個測試案例,輸出a和b之間1出現的次數。 spa
樣例輸入:0 5 1 13 21 55 31 99樣例輸出:
1 6 4 7分析:
次題爲編程之美上的經典例題從0開始到某個數N有多少個1的變形; .net
方法一:
遍歷0到N的每一個數統計每一個數中1出現的個數,可是這個算法的致命問題是效率,它的時間複雜度是O(N)*計算一個整數數字裏面「1」的個數的複雜度=O(N*logN) code
方法二:(參考:http://blog.csdn.net/zcsylj/article/details/6393315) blog
仔細分析這個問題,給定了N,彷佛就能夠經過分析「小於N的數在每一位上可能出現1的次數」之和來獲得這個結果。讓咱們來分析一下對於一個特定的N,如何獲得一個規律來分析在每一位上全部出現1的可能性,並求和獲得最後的f(N)。 it
先從一些簡單的狀況開始觀察,看能不能總結出什麼規律。 io
先看1位數的狀況。
若是N=3,那麼從1到3的全部數字:一、二、3,只有個位數字上可能出現1,並且只出現1次,進一步能夠發現若是N是個位數,若是N>=1,那麼f(N)都等於1,若是N=0,則f(N)爲0。
再看2位數的狀況。
若是N=13,那麼從1到13的全部數字:一、二、三、四、五、六、七、八、九、十、十一、十二、13,個位和十位的數字上均可能有1,咱們能夠將它們 分開考慮,個位出現1的次數有兩次:1和11,十位出現1的次數有4次:十、十一、12和13,因此f(N)=2+4=6。要注意的是11這個數在十位和個位都出現了1,可是11剛好在個位爲1和十位爲1中被計算了兩次,因此不用特殊處理,是對的。再 考慮N=23的狀況,它和N=13有點不一樣,十位出現1的次數爲10次,從10到19,個位出現1的次數一、十一、21,因此f(N)=3+10=13。 經過對兩位數進行分析,咱們發現,個位出現1的次數不只和個位數字有關,還和十位數有關:若是N的個位數大於等於1,則個位出現1的次數爲十位數的數字加 1;若是N的個位數爲0,則個位出現1的次數等於十位數的數字。而十位數字上出現1的次數不只和十位數有關,還和個位數有關:若是十位數字等於1,則十位 數上出現1的次數爲個位數的數字加1;若是十位數大於1,則十位數上出現1的次數爲10。
例如:
f(13)=個位出現1的個數+十位出現1的個數=2+4=6;
f(23)=個位出現1的個數+十位出現1的個數=3+10=13;
f(33)=個位出現1的個數+十位出現1的個數=4+10=14;
……
f(93)=個位出現1的個數+十位出現1的個數=10+10=20;
接着分析3位數。
若是N=123:
個位出現1的個數爲13:1,11,21,…,91,101,111,121
十位出現1的個數爲20:10~19,110~119
百位出現1的個數爲24:100~123
f(123)=個位出現1的個數+十位出現1的個數+百位出現1的次數=13+20+24=57;
同理咱們能夠再分析4位數、5位數。
讀者朋友們能夠寫一寫,總結一下各類狀況有什麼不一樣。
根據上面的一些嘗試,下面咱們推導出通常狀況下,從N獲得f(N) 的計算方法:
假設N=abcde,這裏a、b、c、d、e分別是十進制數N的各個位數上的數字。若是要計算百位上出現1的次數,它將會受到三個因素的影響:百位上的數字,百位如下(低位)的數字,百位(更高位)以上的數字。
若是百位上的數字爲0,則能夠知道,百位上可能出現1的次數由更高位決定,好比12013,則能夠知道百位出現1的狀況多是100~199,1 100~1 199,2 100~2 199,…,11 100~11 199,一共有1200個。也就是由更高位數字(12)決定,而且等於更高位數字(12)*當前位數(100)。
若是百位上的數字爲1,則能夠知道,百位上可能出現1的次數不只受更高位影響,還受低位影響,也就是由更高位和低位共同決定。例如對於12113,受更高位影響,百位出現1的狀況是100~199,1 100~1 199,2 100~2 199,…,11 100~11 199,一共有1200個,和上面第一種狀況同樣,等於更高位數字(12)*當前位數(100)。它還受低位影響,百位出現1的狀況是12 100~12 113,一共114個,等於低位數字(113)+1。
若是百位上數字大於1(即爲2~9),則百位上可能出現1的次數也僅由更高位決定,好比12 213,則百位出現1的可能性爲100~199,1 100~1 199,2 100~2 199,…,11 100~11 199,12 100~12 199,一共有1300個,而且等於更高位數字+1(12+1)*當前位數(100)。
經過上面的概括和總結,咱們能夠寫出以下的更高效算法來計算f(N):
其算法思路是:經過對數字進行有規律的總結,發現從1到N,中出現的全部的1的總數。能夠從N這個數總結出來的。那麼出現1的總數應該等於,個位上出現1的次數+十位上出現1的次數+百位上出現1的次數+。。。。。。
因此對於一個數abcde,取百位上的c來計算,
倘若c是"1",那麼百位上1的個數是由他的高位和低位來決定的。等於ab*100+cde+1;
倘若c是"0",那麼百位上1的個數是ab*100;
假如c是大於1,那麼 百位上1的個數是(ab+1)*100;
Java實現:import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.StreamTokenizer; public class Main { public static int sum(int n){ if(n < 1)return 0; int count = 0; int Factor = 1; int LowerNum = 0; int CurrNum = 0; int HigherNum = 0; while(n/Factor != 0){ LowerNum = n - (int)Math.floor(n/Factor)*Factor; CurrNum = (n/Factor)%10; HigherNum = n/(Factor*10); switch(CurrNum){ case 0: count += HigherNum*Factor; break; case 1: count += HigherNum*Factor + LowerNum + 1; break; default: count += (HigherNum + 1) * Factor; break; } Factor *= 10; } return count; } public static void main(String[] args) throws IOException { StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in))); while(st.nextToken() != StreamTokenizer.TT_EOF){ int a = (int) st.nval; st.nextToken(); int b = (int) st.nval; if(a < b) System.out.println(sum(b) - sum(a-1)); else System.out.println(sum(a) - sum(b-1)); } } }