编码练习: 函数求值

题目:函数求值

附加描述

假设存在一个正整数M,用Func(M)表示所有<=M的自然数中所有位数为1和2的个数之和。
例如:当M为12时,得到的所有自然数为1,2,3,4,5,6,7,8,9,10,11,12;所有位数为1和2的个数之和为7(1,2,10,11,12),因此 Func(12)=7。
输入M,求Func(M)的值,若Func(M)得到的值超过20123,则算出Func(M)mod20123的值

样例输入

9
10
11
12
13

样例输出

2
3
5
7
8

这题和《剑指offer》的面试题#43类似,不过原题是求1~n整数中1出现的次数。

解法1

暴力法,直接遍历从1~n的整数,判断每个整数的每一位出现1和2的总数,进行累加。
取模(%)判断最低位是否为1或2,将该整数/10,循环判断最低位直到该整数变为0。

/*时间复杂度为O(N*LogN)*/
int Func(int M){
	int count = 0;	/*计数*/
	int temp;
	
	if(M <= 0){
		return 0;
	}
		
	for(int i = 1; i <= M; i++){
		temp = i;
		while(temp > 0){
			if((temp % 10) == 1 || (temp % 10) == 2){
				count++;
			}
			
			temp /= 10;
		}
	}
	
	return count;
}

解法2

找数字的规律,在《剑指offer》面试题#43的解法上进行小修改。
思路是参考《剑指offer》的。
以31345为例:

graph TD; 31345 --> 1到1345; 31345 --> 1346到31345;

为了方便递归实现,将一个数拆分成除最高位外的部分和剩余部分(31345拆为1-1345和1346-31345两部分,这样去掉最高位的3就剩下了1345)。
对于1346-31345,目前只考虑最高位(万位)的情况,1只会出现在1000019999区间,出现10000次,2只会出现在2000029999区间,出现10000次。

列几个数看看:
对于12345这样的数,最高位是1,那么只考虑最高位的情况下,1出现的次数是2345+1=2346次,2出现的次数是0次;
对于22345这样的数,最高位是2,那么只考虑最高位的情况下,1出现的次数是10000次,2出现的次数是2345+1=2346次;
对于32345这样的数,最高位是3,那么只考虑最高位的情况下,1出现的次数是10000次,2出现的次数是10000次;
对于42345这样的数,最高位是4,那么只考虑最高位的情况下,1出现的次数是10000次,2出现的次数是10000次;
我们需要对整数的最高位进行判断,是等于1,等于2,还是大于2。然后递归依次判断剩余位数即可。

/*对数字找规律*/
int NumberOf1And2Between1AndN(int n){
	if(n <= 0)
		return 0;
	
        /*将数字转为字符串进行处理*/
	char str[40];
	sprintf(str, "%d", n);
	return NumberOf1And2(str);
}

int NumberOf1And2(const char* str){
        /*字符指针为空,或字符非法,或到了字符串末尾,则直接返回0*/
	if(!str || *str < '0' || *str > '9' || str == '\0'){
		return 0;
	}
	
	int first = *str - '0';
	unsigned int length = strlen(str);
	
        /*以下判断主要作为递归的终止条件*/
	if(length == 1&& first ==0)
		return 0;
	else if(length == 1 && first == 1)
		return 1;
	else if(length == 1 && first >= 2)
		return 2;
	
	int numFistDigit = 0;
	if(first > 2){
                /*1出现PowerBase10(length - 1)次,2出现PowerBase10(length - 1)次,比如31345,最高位的1出现10^4次。*/
		numFistDigit = 2 * PowerBase10(length - 1);
	}else if(first == 1){
		numFistDigit = atoi(str + 1) + 1;
	}else if(first == 2){
		numFistDigit = PowerBase10(length - 1) + atoi(str + 1) + 1;
	}
	
        /*以31345为例,固定最高位,剩下4位数中,固定其中的1位数来选1或者选2,剩余3位中,每一位都有10种可能*/
	int numOtherDigits = 2 * first * (length - 1) * PowerBase10(length - 2);
	
	/*递归处理除最高位外其它位*/
	int numRecursive = NumberOf1And2(str+1);
			
	return numFistDigit + numOtherDigits + numRecursive;
}

int PowerBase10(unsigned int n){
        /*计算10的n次方---简洁版本*/
	int result = 1;
	for(unsigned int i = 0; i < n; ++i)
		result *= 10;
	
	return result;
}
posted @ 2020-09-27 22:41  莱纳你坐啊  阅读(249)  评论(0编辑  收藏  举报