1. 程式人生 > >劍指offer面試題[32]:從1到n整數中1出現的次數

劍指offer面試題[32]:從1到n整數中1出現的次數

題目描述

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

(題目意思是:輸入一個整數n,求從1到n這n個整數的十進位制表示中1出現的次數)

思路分析:

思路一:(不考慮時間效率的解法,靠它想拿offer有點難)

       從1到n遍歷求每個數字中1的個數,然後累加,每個數用求餘運算(%),若餘數為1,則說明該位為1。大多數人應該可以求解。

思路二:(從數字規律著手明顯提高效率的做法,能讓面試官耳目一新)

      如果希望不用計算每個數字的1的個數,那就只能去尋找1在數字中出現的規律了。為了找到規律,我們不妨用一個稍微大一點的數字如21345作為例子來分析。我們把從1到21345分為兩段,一段是從1到1345,另一段是從1346到21345。

      我們先看從1346到21345中1出現的次數。1的出現分兩種情況。首先分析1出現在最高位(本例中是萬位)的情況。從1346到21345的數字中,1出現在10000到19999這10000個數字的萬位中,一共出現了10000(10^4)個。

      值得注意的是,並不是對所有5位數而言在萬位出現的次數都是10000個。對於萬位是1的數字比如輸入12345,1只出現在10000到12345的萬位,出現的次數不是10^4次,而是2346次,也就是除去最高數字位之後剩下的數字再加上1(即2345+1=2346)。

       接下來分析1出現在除最高位之外的其他四位數中的情況。本例中1346到21345這2000個數字後四位中1出現的次數是8000次。由於最高位是2,我們可以再把1346到21345分為兩段,1346到11345和11346到213456.每一段剩下的4位數字中,選擇其中一位是1,其餘三位可以在0~9這10個數字中任選,因此根據排序組合的原則,總共出現的次數是2*4*10^3=8000次。

        至於1到1345中1出現的次數,我們就可以根據遞迴求得了。這也就是為什麼把1~21345分為1~1345和1346~21345兩段的原因。因為把21345的最高位去掉就變為1345,便於我們採用遞迴的思路。

       參考程式碼如下:

#include <iostream>
#include <stdio.h>
using namespace std;

class Solution {
public:
	/*方法1:
	int NumberOf1Between1AndN_Solution(int n)
	{
	if(n<=0)
	return 0;
	int number=0;
	for(int i=1;i<=n;i++)
	{
	int temp=i;
	while(temp)    //注意這裡一定是用一箇中間臨時變數來存取每次讀入的數字i
	{
	int s=temp%10;
	if(s==1)
	number++;
	temp=temp/10;
	}
	}
	return number;
	}
	*/
	//方法2:
	int NumberOf1Between1AndN_Solution(int n)
	{
		if (n <= 0)
			return 0;

		char strN[50];
		sprintf_s(strN, "%d", n);   //為了程式設計方便,將數字轉換為字串

		return NumberOf1(strN);
	}

	int powerBase10(int n)
	{
		int result = 1;
		for (int i = 0; i<n; i++)
			result *= 10;
		return result;
	}

	int NumberOf1(char *strN)
	{
		if (strN == NULL || *strN<'0' || *strN>'9' || *strN == '\0')
			return 0;
		int first = *strN - '0';
		int length = static_cast<int>(strlen(strN));
		if (length == 1 && first == 0)
			return 0;
		if (length == 1 && first>0)
			return 1;

		//假設strN是"21345"
		//numFirstDigit是數字10000~19999的第一位中的數目
		int numFirstDigits = 0;
		if (first>1)
			numFirstDigits = powerBase10(length - 1);
		else if (first == 1)
			numFirstDigits = atoi(strN + 1) + 1;   //atoi是把字串轉換成整型數的一個函式

		//numOtherDigits是1346~21345除了第一位之外的數位中的數目
		int numOtherDigits = first*(length - 1)*powerBase10(length - 2);
		int numRecursive = NumberOf1(strN + 1);

		return numFirstDigits + numOtherDigits + numRecursive;
	}
};

int main()
{
	Solution s;
	int n;
	cin >> n;
	int CountOf1=s.NumberOf1Between1AndN_Solution(n);
	cout << CountOf1 << endl;
	system("pause");
}