九章算法官网-原文网址
http://www.jiuzhang.com/problem/52/
题目
数一数在0到n之间有多少个数字k(0<=k<=9)。如n=12时,[0,1,2...,12]之间一共有5个1。分别包含在[1, 10, 11, 12]之中。
在线测试本题
http://lintcode.com/problem/digit-counts/
解答
本题的解法要诀在于熟练的使用递归思路和预处理。
预处理:
f[0] 代表了0-9中有多少个数字k。(肯定=1)
f[1] 代表了00-99中有多少个数字k。
f[2] 代表了000-999中有多少个数字k。
...
存在公式:f[i] = f[i-1] * 9 + 10i
g[1] 代表了10-99中有多少个数字k。
g[2] 代表了100-999有多少个数字k。
存在公式:g[i] = f[i] - f[i-1] // if k != 0
= f[i] - f[i-1] - 10i // if k ==0
令[a, b]代表a-b之间有多少个数字k。假设n=2345。
[0, 2345] = [0, 999] + [1000, 1999] + [2000, 2345]
这里[0, 999] = [0,9] + [10, 99] + [100, 999] = 1 + g[1] + g[2]
[1000, 1999] = [000, 999] = f[2] // k != 1
[000, 999] + 1000 = f[2] + 1000 // k == 1
[2000,2345] = [000, 345] // k!=2
= [000, 345] + 346 // k==2
[000,345] = [000, 099] + [100, 199] + [200, 299] + [300, 345]
= f[1] + 100 * (k==0) + f[1] + 100 * (k == 1) + f[1] + 100 * (k == 2) + [00,45] + 46 * (k == 3)
根据上面的分析,我们每次可以用O(1)的时间将带计算的n的位数减小一位:
从计算[0, 2345]到计算[000,345]到计算[00,45]。
因此整个算法的时间复杂度为O(log10(n))