使用K个字母查找大小为N的回文总数

时间:2019-05-05 13:44:40

标签: algorithm permutation dynamic-programming palindrome discrete-mathematics

使用K个字母查找长度为N的回文总数,以使长度为2到N-1的任何前缀都不是回文。

尝试K*((K-1)^(Math.ceil((N-2)/2))) 第一位可以容纳K个字母。第二个K-1,除了第一个。第三名也是如此。 由于我们需要用字母填充的一半位置的其余部分将遵循相同的条件以使其成为回文。但这不是正确的解决方案。

2 个答案:

答案 0 :(得分:1)

表示M = \ceil{N / 2}。该公式取决于一个简单的观察结果,即如果长度为N的回文报文的回文前缀为长度小于2的非平凡(即长度至少为N),则其回文前缀为长度不超过M的非平凡回文前缀。

如果我们用f(n)表示长度为n good 回文数(没有回文前缀长度为2和{{1}之间的回文数) }),我们可以通过从回文总数中减去回文数的数量来计算n - 1。根据上面的观察,每个不良回文都有一个{em>最小非平凡回文前缀,其长度f(N)K^ML(包括)之间,这必须是一个好回文。每个2都有M这样的前缀,并且它们中的任何一个都可以通过f(L)的方式扩展为长度为L的回文,所以

N

答案 1 :(得分:0)

我编写了一个Python脚本来检查Łukasz's answer中是否有NK的较小值。

游乐场:https://ideone.com/7Nk41y

import functools
import itertools
import string


@functools.lru_cache()
def f(N, K):
    if N == 2:
        return K
    M = (N + 1)//2
    return K**M - sum(K**(M - L) * f(L, K) for L in range(2, M+1))


def is_palindrome(s):
    return s[:(len(s) + 1)//2] == s[len(s)//2:][::-1]


def brutal_f(N, K):
    alphabet = string.ascii_lowercase[:K]
    ret = 0
    for t in itertools.product(alphabet, repeat=N):
        s = "".join(t)
        if is_palindrome(s) and not any(is_palindrome(s[:i]) for i in range(2, N)):
            ret += 1
    return ret


print(all(
        f(N, K) == brutal_f(N, K)
        for N in range(12)
        for K in range(4)
    ))