解法1:中心扩散
简单,不提
解法2:动态规划,从上往下填表很重要
import numpy as np
class Solution:
def longestPalindrome(self, s: str) -> str:
dp = np.zeros((len(s), len(s)))
l_str = s[0]
l_len = 1
# 状态转移
for i in range(len(s)):
dp[i][i] = 1 # 填对角线
for j in range(i):
if s[i] == s[j] and (dp[j+1][i-1] == 1 or i == j+1):
dp[j][i] = 1
if i-j+1 > l_len:
l_len = i-j+1
l_str = s[j:i+1]
return l_str
上述解法有个优化:记录时不必记录整个回文串,记录起始位置即可,这样可以省去访问时间,最后返回时访问即可。
优化2:使用python list创建二维数组,时间会快很多
dp = [[0 for _ in range(len(s))] for _ in range(len(s))]
解法3:马拉车算法
用中心扩散解法很容易获得以每个元素为中心的最大扩展长度,把他们记录在数组中,取最大就能的到结果。但每次都重复计算很伤。马拉车利用了前面计算的信息来减少运算,最终实现O(N)的复杂度。如何利用前面的信息呢?答案是对称性,kmp算法也是利用前面的计算和实际数组前后相同来做。本算法维护一个最远对称窗口,记录最右扩展的点以及中心。包含在这一窗口内,右边未计算的点可以利用左边的对称信息来解,因为他们的回文程度相同。
如果dp[mirror]的长度超过了maxRight,那么长度一定等于maxRight - i,因为左边边界外元素,回文元素,mirror对应元素都是相同的,而右边maxRight右边的和这三个不可能相同。如果dp[mirror]的长度正好等于maxRight,则需要继续往外扩散看最大长度。这种情况最左边两个元素不一定相同,所以maxRight+1是可能继续回文的。
如果i >= maxRight,左边元素是给不到任何回文信息的,因为左右在边界处不对称。
import numpy as np
class Solution:
def longestPalindrome(self, s: str) -> str:
if len(s) < 1: return s
ss = "#"
for c in s:
ss += c + '#'
maxRight, center = 0, 0
dp = [0 for _ in ss]
def expand(i, j, maxRight, center):
while i+j < len(ss) and ss[i+j] == ss[i-j]:
j += 1
if i+j-1 > maxRight:
maxRight = i+j-1
center = i
dp[i] = j-1
return maxRight, center
for i, c in enumerate(ss):
mirror = 2*center - i
if i >= maxRight or mirror < 0:
maxRight, center = expand(i, 1, maxRight, center)
elif dp[mirror] < maxRight-i:
dp[i] = dp[mirror]
elif dp[mirror] > maxRight-i:
dp[i] = maxRight-i
else:
maxRight, center = expand(i, dp[mirror]+1, maxRight, center)
i = np.argmax(dp)
if dp[i] == 0: return s[0]
ret = ''
for j in range(i-dp[i], i+dp[i]+1):
if ss[j] != '#':
ret += ss[j]
return ret