剑指offer 32-从1到n整数中1出现的次数

我们把从121345的所有数字分成两段,即1-13451346-21345

先来看1346-213451出现的次数。1的出现分为两种情况:一种情况是1出现在最高位(万位)。从121345的数字中,1出现在10000-1999910000个数字的万位中,一共出现了10000104)次;另外一种情况是1出现在除了最高位之外的其他位中。例子中1346-21345,这20000个数字中后面四位中1出现的次数是2000次(2*103,其中2是第一位的数值,103是因为数字的后四位数字其中一位为1,其余的三位数字可以在0910个数字任意选择,由排列组合可以得出总次数是2*103)。(这一段分析有点小问题,这20000个数字中后面四位中1出现的次数应该是8000次,2*4*10^3,其中2是第一位的数值,4是后面四位的位数长度,10^3是因为数字的后四位数字 其中一位为1,其余的三位数字可以在0到9这10个数字任意选择,由排列组合可以得出总次数是2*4*10^3,下面的代码实现,原作者写的是正确的)

至于从11345的所有数字中1出现的次数,我们就可以用递归地求得了。这也是我们为什么要把1-21345分为1-13451346-21345两段的原因。因为把21345的最高位去掉就得到1345,便于我们采用递归的思路。


#include<iostream>
using namespace std;

int PowerBase10(int N)
{
	int s=1;
	for(int i=0;i<N;i++)
		s=s*10;
	return s;
}
int NumberOf1(const char *strN)
{
	if(strN==NULL || *strN=='\0' ||	*strN<'0' ||	*strN>'9' )
		return 0;

	int first = *strN-'0';
	int length = strlen(strN);
	 if(length == 1 && first == 0)
        return 0;
    if(length == 1 && first > 0)
        return 1;

	// 假设strN是"21345"
    // numFirstDigit是数字10000-19999的第一个位中1的数目
	int numFirstDigit = 0; 
	if(first>1)
		numFirstDigit = PowerBase10(length-1);
	else if(first == 1)
		numFirstDigit = atoi(strN+1)+1;

	// numOtherDigits是01346-21345除了第一位之外的数位中1的数目
	int numOtherDigits = first*(length-1)*PowerBase10(length-2);
	// numRecursive是1-1345中1的数目
    int numRecursive = NumberOf1(strN + 1);
	return numFirstDigit + numOtherDigits + numRecursive;
}
int NumberOf1Between1AndN(int n)
{
	if(n<=0)
		return 0;
	char strN[50];
	itoa(n,strN,10);
	return NumberOf1(strN);
}

// ====================测试代码====================
void Test(char* testName, int n, int expected)
{
    if(testName != NULL)
        cout<<testName<<" ";
    
    if(NumberOf1Between1AndN(n) == expected)
        cout<<"passed."<<endl;
    else
        cout<<"failed."<<endl;   
	cout<<endl;  
}

void Test()
{
    Test("Test1", 1, 1);
    Test("Test2", 5, 1);
    Test("Test3", 10, 2);
    Test("Test4", 55, 16);
    Test("Test5", 99, 20);
    Test("Test6", 10000, 4001);
    Test("Test7", 21345, 18821);
    Test("Test8", 0, 0);
}

int main(int argc, char* argv[])
{
    Test();
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值