问题:
我见过这样的问题:
-
count the number of 0s between 0 and N?
-
count the number of 1s between 0 and N?
-
count the number of 2s between 0 and N?
- …………
这类问题非常类似于要求找到 Ks (即 K=0,1,2,...,9)
显示在数字范围 中的总数[0, N]
.
例子:
- 输入:
K=2, N=35
- 输出:
14
- 详细信息:
[0,35]
之间的2
列表:2、12、20、21、22、23、24、25、26 , 27, 28, 29, 32
,注意22
会被算作两次(因为22
包含两个2
)
我们有什么:
它们中的每一个都有解决方案(如果您搜索它就可以找到)。通常,需要O(log N)
时间通过递归考虑最高位来解决此类问题,依此类推。计算 0 和 N 之间 2 的个数的一个示例可以通过以下过程解决(借用自 here ):
// Take n = 319 as example => 162
int numberOf2sBetween0AndN(int n)
{
if (n < 2)
return 0;
int result = 0;
int power10 = 1;
while (power10 * 10 < n)
power10 *= 10;
// power10 = 100
int msb = n / power10; // 3
int reminder = n % power10; // 19
/*** Count # of 2s from MSB ***/
if (msb > 2) // This counts the first 2 from 200 to 299
result += power10;
if (msb == 2) // If n = 219, this counts the first 2 from 200 to 219 (20 of 2s).
result += reminder + 1;
/*** Count # of 2s from reminder ***/
// This (recursively) counts for # of 2s from 1 to 100; msb = 3, so we need to multiply by that.
result += msb * numberOf2s(power10);
// This (recursively) counts for # of 2s from 1 to reminder
result += numberOf2s(reminder);
return result;
}
问题:
注意,我们不能简单地将上面代码中的2
部分全部改成1
来解决统计1个数的问题
介于 0
和 N
之间。看来我们必须针对不同的情况进行不同的处理(不是微不足道的)。
是否有一个我们可以遵循的通用程序来处理所有 K
(即 K=0,1,2,...,9
),即类似以下函数的东西?
int numberOfKsBetween0AndN(int k, int n)
测试用例:
如果您想检查您的解决方案,这里有一些测试用例:
k=1, N=1
: 1k=1, N=5
: 1k=1, N=10
: 2k=1, N=55
: 16k=1, N=99
: 20k=1, N=10000
: 4001k=1, N=21345
: 18821k=2, N=10
: 1k=2, N=100
: 20k=2, N=1000
: 300k=2, N=2000
: 601k=2, N=2145
: 781k=2, N=3000
: 1900
最佳答案
我相信这就是您所需要的,简单、通用且快速。
下面是 Python 中的示例:
慢速检查器
校验器很简单,使用string
查找string中从'0'到'n'的所有数字,统计k
的匹配次数,比较慢但是我们可以用它来检查其他解决方案。
import string
def knChecker( k, n ):
ct = 0
k = str(k)
for i in xrange(0,n+1):
ct += string.count(str(i),k)
return ct
快速通用的解决方案
k≠0
对于每个 k = [1,9],很明显在 [0,9] 中我们可以在第一位找到 1 个匹配项;
在 [0,99] 中,我们可以在第一位找到 1 个匹配项,在第二位找到 10 个匹配项,所以都是 1*10^1 + 10*10^0 = 20 个匹配项,
在 [0,999] 中,我们可以在第一位找到 1 个匹配项,在第二位找到 10 个匹配项,在第三位找到 100 个匹配项,所以都是 1*10^2 + 10*10^1 + 100*10^0 = 300匹配...
因此我们可以很容易地得出结论,在 [0,10^l - 1] 中,有 l * 10^(l-1)
个匹配项。
更一般的,我们可以在 [0,f * 10^l - 1] 中找到 f*10^(l-1) * l
匹配。
解决方法如下:
例如,n = 'abcd', k = 'k'
- step1:如果n=0或n='',返回0;计算 'a000' 中的匹配项,使用向上公式,l = len(n)
- step2A:如果 a == k,我们知道所有 'bcd' 都匹配,所以添加
bcd
匹配。 - step2B:如果 a > k,我们知道所有 'k***' 都匹配,所以添加
10^(l-1)
匹配项。 - step3:截取第一个bit a,并设置n = 'bcd',转到step1
这里是 k ≠ 0 的代码:
def knSolver( k, n ):
if k == '0':
return knSolver0( n, 0 )
if not n:
return 0
ct = 0
n = int(n)
k = int(k)
l = len(str(n))
f = int(str(n)[:1])
if l > 1:
ct += f * 10 ** (l-2) * (l-1)
if f > k:
ct += 10 ** (l-1)
elif f == k:
ct += n - f * 10 ** (l-1) + 1
return ct + knSolver( k, str(n)[1:])
k = 0
k = 0 有点棘手,因为 0***
等于 ***
并且不允许将它计算为“0”。
所以 k ≠ 0 的解不适合 k = 0。但思路是相似的。
我们可以发现,如果 n < 100,则必须有 n/10 + 1
个匹配项。
如果 n 在 [100,199] 中,它非常类似于 [0,99] 中的 k ≠ 0,有 20 个匹配项;
如果 n 在 [100,999] 中,它非常类似于 [100,999] 中的 k ≠ 0,有 20 * 9 个匹配项;
如果 n 在 [1000,9999] 中,它非常类似于 [1000,9999] 中的 k ≠ 0,有 300 * 9 个匹配...
更一般的,如果 n 在 [10^l,k*10^l-1] 中,它将有 l*10^(l-1)*k
匹配。
解决方法如下:
例如,n = 'abcd', k = '0', 递归步 s
= 0
- step0:如果n = '',返回0;如果 n < 100,返回
n/10+1
; - step1A: n='f(...)', f 是 n 的第一位。 if s > 0,假设我们之前已经处理过第一个bit,那么0可以当作k≠0,所以如果f == 0,所有rest(...)都应该匹配,加(...)+1就匹配.
- step1B: 如果 s > 0 且 f > 0, l = len(n), 我们知道
的第一位将匹配
,以及10 ** (l-1)
0(...)(...)
中的 (l-1) * 10 ** (l-2)
- step2:如果s == 0,计算'f(...)-1'中的匹配项,使用向上公式
- 第 3 步:如果 s > 0,只需在第 2 步中检查 (...) 因为 s == 0,将得到
(f-1) * 10 ** (l-2) * (l-1 )
, (f-1),因为我们不能从0***
开始。 - step4:截取第一个位f,设n = '(...)', s += 1,转step1
这里是 k = 0 的代码:
def knSolver0( n, s ):
if n == '':
return 0
ct = 0
sn = str(n)
l = len(sn)
f = int(sn[:1])
n = int(n)
if n < 100 and s == 0:
return n / 10 + 1
if s > 0 and f > 0:
ct += 10 ** (l-1) + (l-1) * 10 ** (l-2)
elif s > 0 and f == 0:
ct += n + 1
if n >= 100 and s == 0:
ct += 10
for i in xrange(2,l):
if i == l-1:
ct += i * 10 ** (i-1) * (f-1)
else:
ct += i * 10 ** (i-1) * 9
elif s > 0 and f != 0:
ct += (f-1) * 10 ** (l-2) * (l-1)
return int(ct + knSolver0( sn[1:], s+1 ))
测试
print "begin check..."
for k in xrange(0,10):
sk = str(k)
for i in xrange(0,10000):
#knSolver( sk, i )
if knChecker( sk, i ) != knSolver( sk, i ):
print i, knChecker( sk, i ) , knSolver( sk, i )
print "check end!"
测试 n[0,10000] 中的所有 k[0,9],它通过了所有情况。
测试会花费一点时间,因为检查器很慢。如果删除检查器,我笔记本电脑中的所有案例大约需要一秒钟。
关于algorithm - 统计0到N之间的K个数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20945790/