Java求任意非負整數區間中1出現的次數

題目描述

求出1~13的整數中1出現的次數,並算出100~1300的整數中1出現的次數?爲此他特別數了一下1~13中包含1的數字有1、10、11、12、13因此共出現6次,但是對於後面問題他就沒轍了。ACMer希望你們幫幫他,並把問題更加普遍化,可以很快的求出任意非負整數區間中1出現的次數。

我的想法,一開始想用String的contains方法,但是有一個問題,如果是11,contians方法只能記錄一個1,輸出就少一個。

因此我在contians方法下面再調整了一下,如果contians,把這個String再賦給char[]  (因爲String的底層就是char),這樣再來判斷

最後AC的代碼是 61ms  2713K


import java.util.*;
public class Solution {
   public static int NumberOf1Between1AndN_Solution(int n) {
		    
	      //  HashMap map=new HashMap();
	        int sum=0;
	        String s="";
	        
	        for(int i=1;i<=n;i++){
	            s=Integer.toString(i);
	          //  map.add(s);
	            if(s.contains("1")){
	               // sum++;
	                char [] c=s.toCharArray();
	                for(int j=0;j<c.length;j++){
	                	if(c[j]=='1'){
	                		sum++;
	                	}
	                }
	               
	            }
	            
	        }
	        
	        return sum;
	        
	        
	    }
}

看一下別的解法:從數字出發找規律。

【其實開始我也想過這個是有規律的,動手分析了一下200以內的數字】

1-10:1   【1】

10-20:10 11 12 13 14 15 16 17 18 19  【10】

20-100: 21 31 41 51 61 71 81 91  【8】

100-110: 100 101   【1+1】

110-120: 110 111 112 113 114 115 116 117 118 119  【10】

120-200: 120 121 130 131 140 141 150 151 160 161 170 171 180 181 190 191  【2*8】

但是我的想法是,是不是可以先這樣去劃定區間,比如n=50,那麼1+10肯定有了,再從20-50來一個一個計算。

但是我覺得這個想法不夠好,因此用了上面的方法,不優雅,暴力的解決了。



編程之美上給出的規律:

1. 如果第i位(自右至左,從1開始標號)上的數字爲0,則第i位可能出現1的次數由更高位決定(若沒有高位,視高位爲0),等於更高位數字X當前位數的權重10i-1

2. 如果第i位上的數字爲1,則第i位上可能出現1的次數不僅受更高位影響,還受低位影響(若沒有低位,視低位爲0),等於更高位數字X當前位數的權重10i-1+(低位數字+1)。

3. 如果第i位上的數字大於1,則第i位上可能出現1的次數僅由更高位決定(若沒有高位,視高位爲0),等於(更高位數字+1)X當前位數的權重10i-1

二、X的數目

這裏的  X∈[1,9] ,因爲  X=0  不符合下列規律,需要單獨計算。

首先要知道以下的規律:

  • 從 1 至 10,在它們的個位數中,任意的 X 都出現了 1 次。
  • 從 1 至 100,在它們的十位數中,任意的 X 都出現了 10 次。
  • 從 1 至 1000,在它們的百位數中,任意的 X 都出現了 100 次。

依此類推,從 1 至  10 i ,在它們的左數第二位(右數第  i  位)中,任意的 X 都出現了  10 i−1  次。

這個規律很容易驗證,這裏不再多做說明。

接下來以  n=2593,X=5  爲例來解釋如何得到數學公式。從 1 至 2593 中,數字 5 總計出現了 813 次,其中有 259 次出現在個位,260 次出現在十位,294 次出現在百位,0 次出現在千位。

現在依次分析這些數據,首先是個位。從 1 至 2590 中,包含了 259 個 10,因此任意的 X 都出現了 259 次。最後剩餘的三個數 2591, 2592 和 2593,因爲它們最大的個位數字 3 < X,因此不會包含任何 5。(也可以這麼看,3<X,則個位上可能出現的X的次數僅由更高位決定,等於更高位數字(259)X101-1=259)。

然後是十位。從 1 至 2500 中,包含了 25 個 100,因此任意的 X 都出現了  25×10=250  次。剩下的數字是從 2501 至 2593,它們最大的十位數字 9 > X,因此會包含全部 10 個 5。最後總計 250 + 10 = 260。(也可以這麼看,9>X,則十位上可能出現的X的次數僅由更高位決定,等於更高位數字(25+1)X102-1=260)。

接下來是百位。從 1 至 2000 中,包含了 2 個 1000,因此任意的 X 都出現了  2×100=200  次。剩下的數字是從 2001 至 2593,它們最大的百位數字 5 == X,這時情況就略微複雜,它們的百位肯定是包含 5 的,但不會包含全部 100 個。如果把百位是 5 的數字列出來,是從 2500 至 2593,數字的個數與百位和十位數字相關,是 93+1 = 94。最後總計 200 + 94 = 294。(也可以這麼看,5==X,則百位上可能出現X的次數不僅受更高位影響,還受低位影響,等於更高位數字(2)X103-1+(93+1)=294)。

最後是千位。現在已經沒有更高位,因此直接看最大的千位數字 2 < X,所以不會包含任何 5。(也可以這麼看,2<X,則千位上可能出現的X的次數僅由更高位決定,等於更高位數字(0)X104-1=0)。

到此爲止,已經計算出全部數字 5 的出現次數。

總結一下以上的算法,可以看到,當計算右數第  i  位包含的 X 的個數時:

  1. 取第  i  位左邊(高位)的數字,乘以  10 i−1 ,得到基礎值  a 。
  2. 取第  i  位數字,計算修正值
    1. 如果大於 X,則結果爲  a+ 10 i−1
    2. 如果小於 X,則結果爲  a 。
    3. 如果等 X,則取第  i  位右邊(低位)數字,設爲  b ,最後結果爲  a+b+1 。

相應的代碼非常簡單,效率也非常高,時間複雜度只有  O( log 10 n) 。


大神的代碼,38ms,528K

import java.util.*;
public class Solution {
   public int NumberOf1Between1AndN_Solution(int n) {
        int count = 0;//1的個數
        int i = 1;//當前位
        int current = 0,after = 0,before = 0;
        while((n/i)!= 0){           
            current = (n/i)%10; //高位數字
            before = n/(i*10); //當前位數字
            after = n-(n/i)*i; //低位數字
            //如果爲0,出現1的次數由高位決定,等於高位數字 * 當前位數
            if (current == 0)
                count += before*i;
            //如果爲1,出現1的次數由高位和低位決定,高位*當前位+低位+1
            else if(current == 1)
                count += before * i + after + 1;
            //如果大於1,出現1的次數由高位決定,//(高位數字+1)* 當前位數
            else{
                count += (before + 1) * i;
            }    
            //前移一位
            i = i*10;
        }
        return count;
    }
}









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