📅  最后修改于: 2023-12-03 14:56:01.900000             🧑  作者: Mango
给定一个字符串,找出其最长回文子序列的长度,要求这个子序列的长度为偶数,并且不存在两个相邻字符相同。
例如,对于字符串 "abzcdbz",最长回文子序列为 "bzdzb",长度为 6。
要求最长回文子序列,很容易想到用动态规划来解决。我们可以定义一个二维数组 dp,其中 dp[i][j] 表示字符串区间 [i,j] 的最长回文子序列的长度。对于一个长度为 n 的字符串,最终要求的就是 dp[0][n-1]。
首先,对于长度为 1 的字符串,它自己构成了一个回文子序列,因此 dp[i][i] = 1。对于长度为 2 的字符串,如果两个字符不相同,则长度为 2 的子序列自己也构成了一个回文子序列,因此 dp[i][i+1] = 2;如果两个字符相同,则长度为 2 的子序列为空,即 dp[i][i+1] = 0。
对于长度大于等于 3 的字符串,如果 s[i] == s[j],则在区间 [i+1,j-1] 内一定可以找到一个回文子序列,使得将 s[i] 和 s[j] 加上之后形成一个更长的回文子序列。因此,如果 s[i] == s[j],则 dp[i][j] = dp[i+1][j-1] + 2。
如果 s[i] != s[j],那么此时 dp[i][j] 的值一定不会包含 s[i] 和 s[j],因此 dp[i][j] 应该等于 dp[i+1][j] 和 dp[i][j-1] 中的较大值。
到这里我们解决了如何求出最长回文子序列的长度,但这只是不考虑没有两个相邻字符相同的情况。现在我们要考虑如何限制最长回文子序列的长度为偶数,并且不存在两个相邻字符相同。
我们可以先找出最长的回文子序列的长度,然后从左到右依次判断每个字符是否满足条件。如果当前字符和它的前一个字符相同,那么就将最长回文子序列的长度减 1。
最终得到的就是最长且没有两个相邻字符相同的偶数长度的回文子序列的长度。
def longest_palindromic_subsequence(s: str) -> int:
n = len(s)
dp = [[0] * n for _ in range(n)]
for i in range(n):
dp[i][i] = 1
for i in range(n-1):
dp[i][i+1] = 2 if s[i] != s[i+1] else 0
for l in range(3, n+1, 2):
for i in range(n-l+1):
j = i + l - 1
if s[i] == s[j]:
dp[i][j] = dp[i+1][j-1] + 2
else:
dp[i][j] = max(dp[i+1][j], dp[i][j-1])
length = dp[0][n-1]
for i in range(n):
if i == 0:
continue
if s[i] == s[i-1]:
length -= 1
return length
动态规划的时间复杂度为 $\mathcal{O}(n^2)$。
动态规划使用了长度为 $n^2$ 的二维数组 dp,因此空间复杂度为 $\mathcal{O}(n^2)$。