📅  最后修改于: 2023-12-03 15:40:39.337000             🧑  作者: Mango
回文子序列是指一个序列从左往右和从右往左读都一样的子序列,而奇数长度的回文子序列中心只有一个元素。
本文介绍了求每个中心周围奇数长度回文子序列的总数的方法。
对于一个序列 $S$,我们定义 $P_i$ 表示以 $i$ 为中心的最长回文子序列的半径(即子序列长度的一半)。
对于每个位置 $i$,我们要求出以 $i$ 为中心的所有奇数长度的回文子序列数目 $O_i$。假设 $S$ 的长度为 $n$,则 $i$ 在序列中的位置可能有以下三种情况:
$i$ 是一个单独的字符,即 $P_i = 0$。此时 $O_i = 1$,因为只有 $S_i$ 自身构成的回文子序列。
$i$ 与之前的字符构成一个偶数长度的回文子序列,即 $P_i = P_{i'}$ ($i'$ 表示与 $i$ 对称的位置)。此时 $O_i$ 应该等于以 $i$ 为中心的最长回文子序列中心在 $i'$ 左边的所有奇数长度回文子序列总数,加上以 $i$ 为中心的最长回文子序列中心在 $i'$ 右边的所有奇数长度回文子序列总数。这个数量可以通过动态规划来计算,具体可参考:动态规划解决最长回文子序列问题
$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
以上样例均通过测试。