【九度OJ1373】|【劍指offer32】整數中1出現的次數(從1到n整數中1出現的次數)

題目描述: java

親們!!咱們的外國友人YZ這幾天老是睡很差,初中奧數裏有一個題目一直困擾着他,特此他向JOBDU發來求助信,但願親們能幫幫他。問題是:求出1~13的整數中1出現的次數,並算出100~1300的整數中1出現的次數?爲此他特別數了一下1~13中包含1的數字有110111213所以共出現6,可是對於後面問題他就沒轍了。ACMer但願大家幫幫他,並把問題更加廣泛化,能夠很快的求出任意非負整數區間中1出現的次數。 算法

輸入:

輸入有多組數據,每組測試數據爲一行。 編程

每一行有兩個整數a,b(0<=a,b<=1,000,000,000) 測試

輸出:

對應每一個測試案例,輸出ab之間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));
		}
		
	}

}
相關文章
相關標籤/搜索