题目:
求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。
分析:
可以编写一个判断一个数字有多少个1的函数,然后遍历1-n依次调用统计函数,最后求和即可,不过这样时间复杂度有些多。
我们可以按数字的位数来依次统计1出现的个数,例如21345这个数,我们先看1346-21345区间1出现的个数。
先统计万位出现1的个数,我们知道,10000-19999,万位共出现了10000次,也就是10^4个1,考虑一下特殊情况,如果是12345这样的数,实际上万位出现1的个数就等于万位后面的数字加1,也就是2345+1=2346次。
再来计算后面,也就是其余4位1出现的次数1346-21345可以拆成两部分来看,一部分是1346-11345,每一段剩下的4位数字中,选择1位是1,其余三位可以选择0-9,也就是出现了10^3次,共4位所以也就是4*10^3次,同理剩下的部分11346-21345也是4*10^3次。
而剩下的1-1345区间出现1的个数可以通过递归求得。
程序:
C++
class Solution { public: int NumberOf1Between1AndN_Solution(int n) { int length = 0; int num = n; while(num){ num /= 10; length++; } return helper(n, length); } int helper(int num, int length){ if(length == 0 || num <= 0) return 0; int numfirst = 0; int first = num / pow(length-1); if(length == 1 && first > 0) return 1; if(first > 1){ numfirst = pow(length-1); } if(first == 1){ numfirst = num - pow(length-1) + 1; } int numother = first * (length - 1) * pow(length-2); return numfirst + numother + helper(num - first*pow(length-1), length-1); } int pow(int num){ int res = 1; for(int i = 0; i < num; ++i) res *= 10; return res; } };
Java
public class Solution { public int NumberOf1Between1AndN_Solution(int n) { if(n <= 0) return 0; int res = 0; while(n != 0){ res += helper(n); n--; } return res; } public int helper(int n){ int count = 0; while(n != 0){ if(n % 10 == 1) count++; n /= 10; } return count; } }