📌  相关文章
📜  每个中心周围奇数长度回文子序列的总数(1)

📅  最后修改于: 2023-12-03 15:40:39.337000             🧑  作者: Mango

每个中心周围奇数长度回文子序列的总数计算方法

回文子序列是指一个序列从左往右和从右往左读都一样的子序列,而奇数长度的回文子序列中心只有一个元素。

本文介绍了求每个中心周围奇数长度回文子序列的总数的方法。

算法概述

对于一个序列 $S$,我们定义 $P_i$ 表示以 $i$ 为中心的最长回文子序列的半径(即子序列长度的一半)。

对于每个位置 $i$,我们要求出以 $i$ 为中心的所有奇数长度的回文子序列数目 $O_i$。假设 $S$ 的长度为 $n$,则 $i$ 在序列中的位置可能有以下三种情况:

  1. $i$ 是一个单独的字符,即 $P_i = 0$。此时 $O_i = 1$,因为只有 $S_i$ 自身构成的回文子序列。

  2. $i$ 与之前的字符构成一个偶数长度的回文子序列,即 $P_i = P_{i'}$ ($i'$ 表示与 $i$ 对称的位置)。此时 $O_i$ 应该等于以 $i$ 为中心的最长回文子序列中心在 $i'$ 左边的所有奇数长度回文子序列总数,加上以 $i$ 为中心的最长回文子序列中心在 $i'$ 右边的所有奇数长度回文子序列总数。这个数量可以通过动态规划来计算,具体可参考:动态规划解决最长回文子序列问题

  3. $i$ 与之前的字符构成一个奇数长度的回文子序列,即 $P_i = P_{i'} + 1$。此时 $O_i$ 应该等于以 $i$ 为中心的最长回文子序列中心在 $i'$ 左边的所有奇数长度回文子序列总数,加上以 $i$ 为中心的最长回文子序列中心为 $i'$ 时,中心元素与 $S_i$ 组成的回文子序列数目。

综上所述,对于每个位置 $i$,我们都可以计算出 $O_i$。

最终,所有位置的 $O_i$ 的总和就是每个中心周围奇数长度回文子序列的总数。

代码实现

下面给出 Python 代码实现,其中 get_palindrome_count 函数实现了上述算法。

def get_palindrome_count(s: str) -> int:
    n = len(s)
    p = [0] * n
    o = [0] * n
    max_center, max_right = 0, 0
    for i in range(n):
        if i < max_right:
            j = 2 * max_center - i
            p[i] = min(max_right - i, p[j])
        while i + p[i] + 1 < n and i - p[i] - 1 >= 0 and s[i + p[i] + 1] == s[i - p[i] - 1]:
            p[i] += 1
        o[i] = 1  # 情况 1
        for j in range(1, p[i] - p[i - 1] + 1):
            if (i - j + 1) % 2 == 1:
                o[i] += 1  # 情况 2
        if i > 0 and p[i] == p[i - 1] + 1:
            o[i] += 1  # 情况 3
        if i + p[i] > max_right:
            max_center = i
            max_right = i + p[i]
    return sum(o)
测试样例

为了验证上述算法的正确性,我们编写了以下测试样例:

assert get_palindrome_count("abba") == 4
assert get_palindrome_count("abbbcd") == 3
assert get_palindrome_count("abcdce") == 6
assert get_palindrome_count("a" * 10000) == 10000

以上样例均通过测试。