1,问题:
求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数。
2,想法:
(1),例如给定一个真整数n,求1到n中1的个数,例如给定12,那么有1,10,11,12共5个1,
比较容易想到的是,先求出每一个数中1的个数,再求总和,而求每一个数中1的个数的简单代码为:
int numberof1(int n)
{
int number = 0;
while(n)
{
if (n % 10 == 1)
{
number++;
}
n = n / 10;
}
return number;
}然后就是对1到n这n个数没个都调用一次这个函数即可。
但是这个算法的时间复杂度比较高,若n很大,代价会比较高。
(2)通过分析数据的规律,例如若n为21345,我们可以把它分开求1的个数:
第一:先求出万位上1的个数,即10000到19999共10000个1;
第二:求出除了万位上1的个数外,其它四位上1的个数,我们把这个过程分成1346到21345和1到1345的过程
因为求1到1345中1的个数可以使用递归了。那么我们就只要求1346到21345中除了万位上外,其它位1的个数。
为2 * 4 * 10 * 10 * 10 = 8000个,为什么呢?
2是因为1346到21345有两万个数,可以看成1346到11345和11346到21345两个过程,每个过程中,在四个位中随便选取一个为1,其它三位
为0-9的10个数,所以每个过程是10*10*10*4 = 4000个。两个过程加起来就是8000个。
这样求会有重复吗?
比如1111这种情况。仔细想想,因为1111算四个1.而重复也只是重复了3次,正好满足4次。因此不会有多计算的情况发生。
接下来就是递归求n=1345的情况了。
3,具体代码为:
class Solution {
public:
int numberoflength(int length)
{
int result = 1;
for (int i = 0; i < length; i++)
{
result *= 10;
}
return result;
}
int numberof1(const char *str)
{
if (*str < '0' || *str > '9' || !str || *str == '\0')
{
return 0;
}
int first = *str - '0';
unsigned int length = static_cast<unsigned int>(strlen(str));
if (length == 1 && first == 0)
{
return 0;
}
if (length == 1 && first > 0)
{
return 1;
}
//假设str是21345
//那么我们先求万位上1的个数即10000到19999
int numberofhigh = 0;
if (first == 1)
{
numberofhigh = atoi(str + 1) + 1;//1346
}
else if (first > 1)
{
numberofhigh = numberoflength(length - 1);
}
//接下来求1346到21345中除最高位外的其他四位上1的个数
int numberofother = 0;
numberofother = first * (length - 1) * numberoflength(length - 2);
//接下来就是求1到1345中1的个数,可以用递归了
int numberoflow = 0;
numberoflow = numberof1(str + 1);
return numberofhigh + numberofother + numberoflow;
}
int NumberOf1Between1AndN_Solution(int n)
{
if (n <= 0)
{
return 0;
}
char str[50];
sprintf(str, "%d", n);
return numberof1(str);
}
};
从这个算法中学到的额外知识点:
//string str = "123456";
//str + 1;//这种加法是不行的,c++没有重载字符串加整数的运算符
//string s = "1234" + 1;//这样是可以的,输出是234
char str[] = "12345";
char *s = str + 1;//这样是可以的,相当于字符指针所指位置加1,s为2345
cout << s << endl;//
system("pause");
return 0;
字符串加整数只能在初始化的时候使用,表示指针移动几位。
字符指针可以和整数做相加操作,表示字符指针所指位置移动几位。