不幸的是13号

时间:2016-10-05 08:30:24

标签: python

我最近提出了这个问题Unlucky number 13! ,但是没想到有效解决这个问题。

问题陈述:

N作为输入。

  

N可以非常大0 <= N <= 1000000009

查找由不包含“13”的N个字符组成的此类字符串的总数。字符串可以包含0-9的任何整数,重复任意次。

# Example:

# N = 2 :
# output : 99 (0-99 without 13 number)

# N =1 :
# output : 10 (0-9 without 13 number)

我的解决方案:

N = int(raw_input())

if N < 2:
    print 10

else:
    without_13 = 10

    for i in range(10, int('9' * N)+1):
        string = str(i)
        if string.count("13") >= 1:
            continue
        without_13 += 1
    print without_13

输出

输出文件应该包含以新模块1000000009为单位的每个查询的答案。

任何其他有效的方法来解决这个问题?我的解决方案在编码站点上超出了时间限制。

7 个答案:

答案 0 :(得分:5)

我觉得这个问题的设计期望你最初本能地按照你的方式去做。但是,我认为有一种稍微不同的方法会更快。

您可以自己生成包含数字13的所有数字,而无需遍历中间的所有数字。例如:

2位数: 13

3位数位置1: 113 213 313等。

3位数位置2:131 132 133等。

因此,您无需检查0到n * 9之间的所有数字。您只需计算其中包含13的所有数字,直到长度大于N.

这可能不是最快的解决方案(事实上,如果通过使用一些数学技巧无法有效解决这个问题,我会感到惊讶)但我相信它会比你目前的方法更有效。

答案 1 :(得分:4)

我认为这可以通过递归来解决:

ans(n) = { ans([n/2])^2 - ans([n/2]-1)^2 }, if n is even
ans(n) = { ans([n/2]+1)*ans([n/2]) - ans([n/2])*ans([n/2]-1) }, if n is odd

基础案例:

  • ans(0) = 1
  • ans(1) = 10

即使对于像10^9这样的较大输入,它的实现运行速度也非常快(预计其复杂度为O(log[n])而非其他答案的O(n)):

cache = {}

mod = 1000000009

def ans(n):
    if cache.has_key(n):
        return cache[n]

    if n == 0:
        cache[n] = 1
        return cache[n]
    if n == 1:
        cache[n] = 10
        return cache[n]

    temp1 = ans(n/2)
    temp2 = ans(n/2-1)

    if (n & 1) == 0:
        cache[n] = (temp1*temp1 - temp2*temp2) % mod
    else:
        temp3 = ans(n/2 + 1)
        cache[n] = (temp1 * (temp3 - temp2)) % mod

    return cache[n]

print ans(1000000000)

Online Demo

<强>解释

让字符串s具有偶数个数字'n' 设ans(n)为输入n的答案,即不包含子字符串13的字符串数。
因此,长度为s的字符串n的答案可以写为字符串前半部分(ans([n/2]))的答案的乘法和下半部分的答案。 string(ans([n/2])),减去字符串13出现在数字n中间的情况数,即当前半部分的最后一位是1时并且后半部分的第一个数字是3

这可以用数学方式表示为:

ans(n) = ans([n/2])^2 - ans([n/2]-1)*2

类似地,对于输入数字n为奇数的情况,我们可以推导出以下等式:

ans(n) = ans([n/2]+1)*ans([n/2]) - ans([n/2])*ans([n/2]-1)

答案 2 :(得分:3)

这是一个P&amp; C问题。我假设0是有效的字符串,因此是00,000,依此类推,每个处理都与另一个不同。

不包含长度为N的13的字符串总数,不出所料:

(Total Number of strings of length N) - (Total number of strings of length N that have 13 in them)

现在,长度为N的字符串总数很容易,你有10个数字和N个插槽可以放入:10^N

长度为N且长度为13的字符串数量有点棘手。 你认为你可以这样做:

=> (N-1)C1 * 10^(N-2)
=> (N-1) * 10^(N-2)

但是你错了,或者更准确地说,你是在计算某些字符串。例如,您将过度计算其中包含两个或更多13个字符串的字符串集。

您真正需要做的是应用inclusion-exclusion principle来计算其中包含13个字符串的字符串数量,以便它们都包含一次。

如果你把这个问题看作一个集合计数问题,那么你有很多集合:

S(0,N): Set of all strings of Length N.
S(1,N): Set of all strings of Length N, with at least one '13' in it.
S(2,N): Set of all strings of Length N, with at least two '13's in it.
...
S(N/2,N): Set of all strings of Length N, with at least floor(N/2) '13's in it.

你想要所有字符串的集合,其中包含13,但最多只计算一次。您可以使用包含 - 排除原则来计算该集合。

答案 3 :(得分:2)

事实上,这个问题更多的是关于数学而不是关于python。 对于N个数字,有10 ^ N个可能的唯一字符串。为了得到问题的答案,我们需要减去包含&#34; 13&#34;的字符串的数量。 如果字符串从&#34; 13&#34;我们有10 ^(N-2)个可能的唯一字符串。如果我们在第二个位置有13个(例如像x13那样的字符串......),我们再次有10 ^(N-2)种可能性。但是我们不能继续这种逻辑,因为这将导致我们对不同位置的13的字符串进行双重计算。例如,对于N = 4,将有一个字符串&#34; 1313&#34;我们将计算两次。为了避免这种情况,我们应该只计算我们以前没有计算过的那些字符串。所以对于&#34; 13&#34;在possition p(从0开始),我们应该找到没有的唯一字符串的数量&#34; 13&#34;在p的左侧,即每个p number_of_strings_for_13_at_p = total_number_of_strings_without_13(N = p-1)* 10 ^(N-p-2) 所以我们recursevily定义total_number_of_strings_without_13函数。

以下是代码中的想法:

def number_of_strings_without_13(N):
    sum_numbers_with_13 = 0
    for p in range(N-1):
        if p < 2:
            sum_numbers_with_13 += 10**(N-2)
        else:
            sum_numbers_with_13 += number_of_strings_without_13(p) * 10**(N-p-2)

    return 10**N - sum_numbers_with_13

我应该说10**N在N的力量中意味着10。所有其他的如上所述。这些函数还具有令人惊讶的能力,能够为N = 1和N = 2提供正确的答案。

为了测试这是正确的,我已经将代码重写为函数并重构了一点:

def number_of_strings_without_13_bruteforce(N):
    without_13 = 0
    for i in range(10**N):
        if str(i).count("13"):
            continue
        without_13 += 1
    return without_13

for N in range(1, 7):
    print(number_of_strings_without_13(N),
          number_of_strings_without_13_bruteforce(N))

他们给出了相同的答案。随着更大的N暴力非常缓慢。但是对于非常大的N递归函数也会变慢。有一个众所周知的解决方案:因为我们使用number_of_strings_without_13的值多次参数小于N,我们应该记住答案,而不是每次重新计算它们。这样做非常简单:

def number_of_strings_without_13(N, answers=dict()):
    if N in answers:
        return answers[N]

    sum_numbers_with_13 = 0
    for p in range(N-1):
        if p < 2:
            sum_numbers_with_13 += 10**(N-2)
        else:
            sum_numbers_with_13 += number_of_strings_without_13(p) * 10**(N-p-2)

    result = 10**N - sum_numbers_with_13
    answers[N] = result
    return result

答案 4 :(得分:2)

f(n)为长度为n且其中没有“13”的序列数,g(n)为长度为n的序列数,其中包含“13”。

然后f(n) = 10^n - g(n)(用数学符号表示),因为它是可能的序列数(10^n)减去包含“13”的序列。

基本情况:

f(0) = 1
g(0) = 0
f(1) = 10
g(1) = 0

当使用“13”查找序列时,序列的开头可以有一个“13”。这将考虑10^(n-2)可能的序列,其中包含“13”。它也可能在第二个位置有一个“13”,再次考虑10^(n-2)可能的序列。但是如果它在第三位置有一个“13”,并且我们假设也会有10^(n-2)个可能的序列,那么我们可能会有两次已经有“13”的第一个位置。所以我们必须减去它们。相反,我们将10^(n-4)次计算为f(2)(因为这些正好是前两个位置中没有“13”的组合)。

E.g。对于g(5):

g(5) = 10^(n-2) + 10^(n-2) + f(2)*10^(n-4) + f(3)*10^(n-5)

我们可以在任何地方改写它看起来一样:

g(5) = f(0)*10^(n-2) + f(1)*10^(n-3) + f(2)*10^(n-4) + f(3)*10^(n-5)

或者只是f(i)*10^(n-(i+2))i之和的范围从0n-2

在Python中:

from functools import lru_cache

@lru_cache(maxsize=1024)
def f(n):
    return 10**n - g(n)

@lru_cache(maxsize=1024)
def g(n):
    return sum(f(i)*10**(n-(i+2)) for i in range(n-1))  # range is exclusive

lru_cache是可选的,但在处理递归时通常是个好主意。

>>> [f(n) for n in range(10)]
[1, 10, 99, 980, 9701, 96030, 950599, 9409960, 93149001, 922080050]

结果是即时的,适用于非常大的数字

答案 5 :(得分:2)

感谢L3viathan的评论,现在很清楚。逻辑很美。

我们假设a(n)是一些n个数字的字符串,其中没有“13”。如果我们知道n-1的所有好字符串,我们可以在每个字符串的左侧再添加一个数字并计算a(n)。由于我们可以将之前的数字与10个新数字中的任意一个组合,我们将获得10*a(n-1)个不同的字符串。但是我们必须减去字符串的数量,这些字符串现在以“13”开头,我们在上一步中错误地总结为OK。有a(n-2)个错误的adde字符串。所以a(n+1) = 10*a(n-1) - a(n-2)。这就对了。这么简单。

更有趣的是,这个序列可以在不使用公式https://oeis.org/A004189的迭代的情况下计算。但实际上这并没有多大帮助,因为公式需要浮点计算,这将导致舍入并且不起作用对于大n(会给出一些错误的答案)。

然而,原始序列很容易计算,并且不需要存储所有先前的值,只需要存储最后两个值。所以这是代码

def number_of_strings(n):
    result = 0
    result1 = 99
    result2 = 10
    if n == 1:
        return result2
    if n == 2:
        return result1
    for i in range(3, n+1):
        result = 10*result1 - result2
        result2 = result1
        result1 = result
    return result 

这个订单比我之前的建议快几个订单。而内存消耗现在只是O(n)

P.S。如果您使用Python2运行此功能,则最好将range更改为xrange

答案 6 :(得分:-1)

给定一个长度为 N 的字符串 S。字符串 S 由 1-9 的数字组成,考虑字符串索引是从 1 开始的。

您需要将字符串分成多个块,以便第 i 个块包含从 index((i 1) • X +1) 到 min(N, (i + X))(包括两个)的元素。如果一个数字是通过从每个块中选择一个数字并将数字按其块的顺序排列形成的,则该数字是有效的

数量