我们把从1到21345的所有数字分成两段,即1-1345和1346-21345。
先来看1346-21345中1出现的次数。1的出现分为两种情况:一种情况是1出现在最高位(万位)。从1到21345的数字中,1出现在10000-19999这10000个数字的万位中,一共出现了10000(104)次;另外一种情况是1出现在除了最高位之外的其他位中。例子中1346-21345,这20000个数字中后面四位中1出现的次数是2000次(2*103,其中2是第一位的数值,103是因为数字的后四位数字其中一位为1,其余的三位数字可以在0到9这10个数字任意选择,由排列组合可以得出总次数是2*103)。(这一段分析有点小问题,这20000个数字中后面四位中1出现的次数应该是8000次,2*4*10^3,其中2是第一位的数值,4是后面四位的位数长度,10^3是因为数字的后四位数字 其中一位为1,其余的三位数字可以在0到9这10个数字任意选择,由排列组合可以得出总次数是2*4*10^3,下面的代码实现,原作者写的是正确的)
至于从1到1345的所有数字中1出现的次数,我们就可以用递归地求得了。这也是我们为什么要把1-21345分为1-1345和1346-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;
}