參考文獻(xiàn):http://www.cnblogs.com/cyjb/p/digitOccurrenceInRegion.html
一、1的數(shù)目
編程之美上給出的規(guī)律:
1. 如果第i位(自右至左,從1開(kāi)始標(biāo)號(hào))上的數(shù)字為0,則第i位可能出現(xiàn)1的次數(shù)由更高位決定(若沒(méi)有高位,視高位為0),等于更高位數(shù)字X當(dāng)前位數(shù)的權(quán)重10i-1。
2. 如果第i位上的數(shù)字為1,則第i位上可能出現(xiàn)1的次數(shù)不僅受更高位影響,還受低位影響(若沒(méi)有低位,視低位為0),等于更高位數(shù)字X當(dāng)前位數(shù)的權(quán)重10i-1+(低位數(shù)字+1)。
3. 如果第i位上的數(shù)字大于1,則第i位上可能出現(xiàn)1的次數(shù)僅由更高位決定(若沒(méi)有高位,視高位為0),等于(更高位數(shù)字+1)X當(dāng)前位數(shù)的權(quán)重10i-1。
二、X的數(shù)目
這里的 X∈[1,9],因?yàn)?X=0 不符合下列規(guī)律,需要單獨(dú)計(jì)算。
首先要知道以下的規(guī)律:
- 從 1 至 10,在它們的個(gè)位數(shù)中,任意的 X 都出現(xiàn)了 1 次。
- 從 1 至 100,在它們的十位數(shù)中,任意的 X 都出現(xiàn)了 10 次。
- 從 1 至 1000,在它們的百位數(shù)中,任意的 X 都出現(xiàn)了 100 次。
依此類(lèi)推,從 1 至 10i,在它們的左數(shù)第二位(右數(shù)第 i 位)中,任意的 X 都出現(xiàn)了 10i?1次。
這個(gè)規(guī)律很容易驗(yàn)證,這里不再多做說(shuō)明。
接下來(lái)以 n=2593,X=5 為例來(lái)解釋如何得到數(shù)學(xué)公式。從 1 至 2593 中,數(shù)字 5 總計(jì)出現(xiàn)了 813 次,其中有 259 次出現(xiàn)在個(gè)位,260 次出現(xiàn)在十位,294 次出現(xiàn)在百位,0 次出現(xiàn)在千位。
現(xiàn)在依次分析這些數(shù)據(jù),首先是個(gè)位。從 1 至 2590 中,包含了 259 個(gè) 10,因此任意的 X 都出現(xiàn)了 259 次。最后剩余的三個(gè)數(shù) 2591, 2592 和 2593,因?yàn)樗鼈冏畲蟮膫€(gè)位數(shù)字 3 < X,因此不會(huì)包含任何 5。(也可以這么看,3<X,則個(gè)位上可能出現(xiàn)的X的次數(shù)僅由更高位決定,等于更高位數(shù)字(259)X101-1=259)。
然后是十位。從 1 至 2500 中,包含了 25 個(gè) 100,因此任意的 X 都出現(xiàn)了 25×10=250 次。剩下的數(shù)字是從 2501 至 2593,它們最大的十位數(shù)字 9 > X,因此會(huì)包含全部 10 個(gè) 5。最后總計(jì) 250 + 10 = 260。(也可以這么看,9>X,則十位上可能出現(xiàn)的X的次數(shù)僅由更高位決定,等于更高位數(shù)字(25+1)X102-1=260)。
接下來(lái)是百位。從 1 至 2000 中,包含了 2 個(gè) 1000,因此任意的 X 都出現(xiàn)了 2×100=200 次。剩下的數(shù)字是從 2001 至 2593,它們最大的百位數(shù)字 5 == X,這時(shí)情況就略微復(fù)雜,它們的百位肯定是包含 5 的,但不會(huì)包含全部 100 個(gè)。如果把百位是 5 的數(shù)字列出來(lái),是從 2500 至 2593,數(shù)字的個(gè)數(shù)與百位和十位數(shù)字相關(guān),是 93+1 = 94。最后總計(jì) 200 + 94 = 294。(也可以這么看,5==X,則百位上可能出現(xiàn)X的次數(shù)不僅受更高位影響,還受低位影響,等于更高位數(shù)字(2)X103-1+(93+1)=294)。
最后是千位?,F(xiàn)在已經(jīng)沒(méi)有更高位,因此直接看最大的千位數(shù)字 2 < X,所以不會(huì)包含任何 5。(也可以這么看,2<X,則千位上可能出現(xiàn)的X的次數(shù)僅由更高位決定,等于更高位數(shù)字(0)X104-1=0)。
到此為止,已經(jīng)計(jì)算出全部數(shù)字 5 的出現(xiàn)次數(shù)。
總結(jié)一下以上的算法,可以看到,當(dāng)計(jì)算右數(shù)第 i 位包含的 X 的個(gè)數(shù)時(shí):
- 取第 i 位左邊(高位)的數(shù)字,乘以 10i?1,得到基礎(chǔ)值 a。
- 取第 i 位數(shù)字,計(jì)算修正值:
- 如果大于 X,則結(jié)果為 a+10i?1。
- 如果小于 X,則結(jié)果為 a。
- 如果等 X,則取第 i 位右邊(低位)數(shù)字,設(shè)為 b,最后結(jié)果為 a+b+1。
相應(yīng)的代碼非常簡(jiǎn)單,效率也非常高,時(shí)間復(fù)雜度只有 O(log10n)。
三、上代碼
/**
* @param n
* @param x [1,9]
* @return
*/
public int NumberOfXBetween1AndN_Solution(int n,int x) {
if(n<0||x<1||x>9)
return 0;
int high,low,curr,tmp,i = 1;
high = n;
int total = 0;
while(high!=0){
high = n/(int)Math.pow(10, i);// 獲取第i位的高位
tmp = n%(int)Math.pow(10, i);
curr = tmp/(int)Math.pow(10, i-1);// 獲取第i位
low = tmp%(int)Math.pow(10, i-1);// 獲取第i位的低位
if(curr==x){
total+= high*(int)Math.pow(10, i-1)+low+1;
}else if(curr<x){
total+=high*(int)Math.pow(10, i-1);
}else{
total+=(high+1)*(int)Math.pow(10, i-1);
}
i++;
}
return total;
}