给定一个字符串,找到字符串可以使用Python生成的可能代码的数量

时间:2017-08-08 08:48:32

标签: python

如果a = 1,则b = 2,c = 3,...... z = 26。我想找到字符串可以使用Python生成的可能代码的数量

例如:

get_combination('12')
Output: 2
// The possible decodings are "AB", "L"

get_combination('121')
Output: 3
// The possible decodings are "ABA", "AU", "LA"

get_combination('1234')
Output: 3
// The possible decodings are "ABCD", "LCD", "AWD"

这是代码。但时间的复杂性更多。有人可以比这个更简单的解决方案

def get_combination(string):
    def get_branchs(string):
        if not string or len(string) == 1:
            return 0
        if string[0:2] <= '26':
            if '0' not in string[1:3]:
                return 1 + get_branchs(string[1:]) + get_branchs(string[2:])
            else:
                return get_branchs(string[2:])
        else:
            return get_branchs(string[1:])
    return 1 + get_branchs(string)

5 个答案:

答案 0 :(得分:1)

这是我得到的最简单的形式:

def get_num_codes(number):
    if not number:
        return 1
    # If you want to allow for "stray zeros" at the end remove this if block
    if number == "0":
        return 0
    count = get_num_codes(number[1:])
    if number[0] != "0" and len(number) > 1 and number[:2] <= "26":
        count += get_num_codes(number[2:])
    return count

两个版本的性能相当:

%timeit get_combination("123412432414324231123412")
>>> 1000 loops, best of 3: 1.83 ms per loop
%timeit get_num_codes("123412432414324231123412")
>>> 1000 loops, best of 3: 1.87 ms per loop

答案 1 :(得分:1)

您可以进行以下观察:

  • 可能存在无效序列:0应始终以1或2开头。如果违反此规则,则应返回0(或引发异常)
  • 输入中可以增加组合数量的所有最小子序列都以1或2开头。因此,例如,以7开头的字符串将产生与删除该7的相同输入一样多的组合
  • 感兴趣的子序列(允许多个组合)可以彼此隔离,因为它们也有一个结束:例如&#34; 212121212127&#34;:这个序列不能用一个扩展到右边更多的字符允许更多组合,因为结尾7永远不能与输入中的下一个字符组合。
  • 让定义一个&#34;相关的子序列&#34; be:如果被视为单独的输入,将返回2个或更多(组合计数)的任何子序列,如果使一个字符更短(从其任一端删除一个字符),则返回较小的值,并且< em> not 如果在子序列的任一侧长1个字符(即不是任何字符,而是作为来自输入的子序列),则返回更大的值。匹配这些相关子序列的正则表达式是

    [12]*?(?:[3-6]|1[3-9]|(?=.0|$))
    
  • 由一个相关子序列表示的组合数由以下递归形式给出(其中n是子序列的长度):

    C(n) = 1 when n == 1
         = 2 when n == 2
         = C(n-1) + c(n-2) when n > 2
    

    这是Fibonacci系列(由于一个指数位置移位,因为真正的Fibonacci有Fib(2) = 1,但这只是一个细节。

  • 因此,如果我们首先找到最长的相关子序列,我们就会知道要生成多少斐波纳契数(而不是更多!),并且只需要生成一次。然后我们可以得到与子序列长度相对应的Fibonacci数的乘积。

以下是实施这些想法的评论代码:

import re
from operator import mul
from functools import reduce # For Python 3.x

def get_combination(string):
    def fib_to(n):
        fibs = [1, 1] # Real Fibonacci would need [0, 1], but we want fib[2] to be 2.
        for i in range(2, n+1):
            fibs.append(fibs[-1] + fibs[-2])
        return fibs

    # Detect an invalid sequence, where a 0 occurs without a 1 or 2 preceding it  
    if re.search(r"(?<![12])0", string):
        return 0
    # Find all sub sequences where a variation is possible
    sequences = re.findall(r"[12]*?(?:[3-6]|1[3-9]|(?=.0|$))", string)
    if not sequences:
        return 1
    # Get the sizes of all these sequences; it is all we need to know
    sizes = [len(seq) for seq in sequences]
    # Generate Fibonacci numbers up to the maximum needed:
    fib = fib_to(max(sizes))
    # Take the product of the sizes for each of the sequences
    return reduce(mul, [fib[size] for size in sizes], 1)

tests = [ # inputs combined with expected outputs
    ["", 1],
    ["2", 1],
    ["12", 2],
    ["121", 3],
    ["1234", 3],
    ["2121", 5],
    ["210", 1],
    ["210210", 1],
    ["22102210", 4],
    ["219219", 9],
    ["07", 0],
    ["507", 0]
]

for test in tests:
    res = get_combination(test[0])
    if res != test[1]: 
        print("Error - Input: '{}' Output: {} Expected: {}".format(test[0], res, test[1]))
    else:
        print("OK - Input: '{}' Output: {}".format(test[0], res))

repl.it

上查看它

注意测试的边界情况。您的代码未通过其中一些测试。

在撰写本文时,没有任何答案能够为输入产生正确的结果&#34; 07&#34; (应该返回0),并且kashif的答案是唯一一个只能通过该测试的答案。

以下是撰写本文时每个答案的测试结果:repl.it

答案 2 :(得分:0)

此处有dynamic programming的机会:无论以前是什么,字符串的给定后缀(因此最后N个字符)的组合数量都是固定的。

因此,您可以从字符串的结尾开始工作,并存储每个后缀的组合数。 这应该会导致运行时间大幅缩短。

$scope.choices.findIndex(function(x) {
   return x.id==10;
});

答案 3 :(得分:0)

正如Trincot评论的那样,您可以通过向get_branch函数传递偏移来节省字符串切片的时间,因此您不必通过stringstring切片。

所以这是我的修改版本,只使用偏移量作为内部get_branch函数的参数:

def get_combination(string):
    def get_branchs(offset=0):
        if len(string)-offset <= 1:
            return 0
        if string[offset:2+offset] <= '26':
            if '0' not in string[1+offset:3+offset]:
                return 1 + get_branchs(1+offset) + get_branchs(2+offset)
            else:
                return get_branchs(2+offset)
        else:
            return get_branchs(1+offset)
    return 1 + get_branchs()

还有一些剩余的切片要与26进行比较并检查0是否在子字符串中。对索引进行测试是一种替代方案,但我不确定当索引超出界限时这会如何表现(切片不介意,索引访问确实如此)

注意:我希望在回答时能够成为主题,也许codereview最适合这类问题&amp;答案。

答案 4 :(得分:0)

如果我们从字符串的末尾开始而不是从开头开始,问题可分为两部分:

  1. 如果最后一位数字!= 0,请对剩余数字重复此过程,并将计数添加到结果中。
  2. 如果最后两位数小于27,请对剩余数字重复此过程并将计数添加到结果中。
  3. 可以在此处找到更多信息:Count Possible Decodings of a given Digit Sequence

    以下是链接中提到的使用动态编程的C代码的Python版本:

    def decodings(digits):
        n_digits =  len(digits)
        count_table = {}
        for i in range(0,n_digits+1):
            count_table[i]=0
        count_table[0] = 1
        count_table[1] = 1
    
        for i in range(2,n_digits+1):
            #Case 1: If the last digit!=0 , repeat and add the count to the result.
            if digits[i-1] > '0':
                count_table[i] = count_table[i-1]
            #Case 2 : If the last two digits are less than 27, repeat and the add the count to the result.
            if digits[i-2] == '1' or (digits[i-2] == '2' and digits[i-1] < '7'):
                 count_table[i] += count_table[i-2]
    
        return count_table[n_digits]
    
    print decodings('121')
    

    时间复杂度为O(n),空间复杂度为O(n)其中&#39; n&#39;是输入中的位数。