正如孔乙己知道“回”字有四种写法
这个问题也有四种解决的方法
-
暴力法
枚举这个字符串的所有子串,再判断是否为回文。时间复杂度为 O(n^3)
此处不实现,太弱智了 -
中心扩展法
从字符串的每一个字符开始,以此为中心,向两侧扩展,直到不是回文为止。
这种方法的时间复杂度是 O(n^2)。但是要考虑到许多特殊情况。如 bb,abbbb,bbbb 等偶数个的回文,所以逻辑上比较复杂。使用这种方法打败了接近 50% 的 python 提交。
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
r = ''
for i in range(0, len(s)):
temp = s[i]
j = i - 1
k = i + 1
while j >= 0 and k < len(s) and s[j] == s[k]:
temp = s[j] + temp + s[k]
j -= 1
k += 1
# 识别如 abba 这种的回文
if k < len(s) and k == i + 1 and s[k] == s[i]:
temp += s[k]
j = i - 1
k += 1
while j >= 0 and k < len(s) and s[j] == s[k]:
temp = s[j] + temp + s[k]
j -= 1
k += 1
# 识别如 aabbbbaa 这种回文
if 1 < k < len(s) and s[k] == s[i] and temp.count(s[i]) == len(temp):
temp += s[k]
k += 1
while j >= 0 and k < len(s) and s[j] == s[k]:
temp = s[j] + temp + s[k]
j -= 1
k += 1
if len(temp) > len(r):
r = temp
return r
- 动态规划法
假设 dp[i, j] = 1 表示 s[i…j] 为回文,则有 dp[i, j] = 1 —> dp[i+1, j-1] = 1
所以得到状态转移方程
d p [ i , j ] = { d p [ i + 1 , j − 1 ] , s[i] = s[j] 0 , s [ i ] ! = s [ j ] dp[i, j] = \begin{cases} dp[i+1, j-1], & \text {s[i] = s[j]} \\ 0, &\text{$s[i] != s[j] $} \end{cases} dp[i,j]={dp[i+1,j−1],0,s[i] = s[j]s[i]!=s[j]
dp 数组的初始化
dp[i,i] = 1
dp[i,i+1] = 1 if s[i] == s[i+1]
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
if not s or len(s) == 1:
return s
dp = [[False for j in range(len(s))] for i in range(len(s))]
longest = 1
start = 0
# 初始化 dp
for i in range(0, len(s)):
dp[i][i] = True
if i + 1 < len(s) and s[i] == s[i+1]:
dp[i][i+1] = True
start = i
longest = 2
for l in range(3, len(s) + 1):
# 枚举子串长度
for i in range(0, len(s) - l + 1):
# 枚举子串的起始点
j = l + i - 1
if s[i] == s[j] and dp[i+1][j-1]:
dp[i][j] = True
start = i
longest = l
return s[start: start + longest]
- Manacher 算法
该算法的介绍在 https://www.cnblogs.com/grandyang/p/4475985.html
以下是其用 python 的实现( faster than 93.25% of Python online submissions )
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
if not s or len(s) == 1:
return s
t = '$#' + '#'.join(s) + '#'
p = [0] * len(t) # p[i] 表示以 t[i] 为中心的回文的最大半径
mx = 0
id = 0
resLen = 0 # 记录全局最大的半径
resCenter = 0
for i in range(1, len(t)):
p[i] = min(p[2 * id - i], mx - i) if mx > i else 1
while i + p[i] < len(t) and i - p[i] >= 0 and t[i + p[i]] == t[i - p[i]]:
p[i] += 1
if mx < i + p[i] - 1:
mx = i + p[i] - 1
id = i
if resLen < p[i]:
resLen = p[i]
resCenter = i
return s[(resCenter - resLen) // 2: (resCenter - resLen) // 2 + resLen - 1]