Leetcode刻意练习----字符串5
题目
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
示例 2
输入: "cbbd"
输出: "bb"
题解一(暴力算法)
class Solution:
def longestPalindrome(self, s: str) -> str:
if s == None:
return None
if s == s[::-1]:
return s
a = s[0]
l = 1
for m in range(len(s) - 1):
for n in range(m + 1, len(s)):
if s[m:n + 1] == s[m:n + 1][::-1] and n - m + 1 > l:
l = n - m + 1
a = s[m:n + 1]
return a
可见暴力算法是真心的慢,看了一些题解之后,学习了两种高端的算法:马拉车算法和动态规划,以下是摘抄的两个题解中的算法。
题解二(manacher法)
假如字符串是奇数个,那么我们可以通过遍历所有字符串,再对所有字符串进行左右匹配,就像中心扩散方法一样。然后得到长度最大的字符串
但是如果字符串是偶数个,我们无法进行此操作
这个算法的最终要的额一点就是,我们将一个偶数长/奇数长的字符串,构造成新的字符串。
这样我们可以对新字符串的每个字符,进行左右匹配。
def mancher(self, s:str) -> str:
if len(s) < 2:
return s
# 将一个可能是偶数长/奇数长的字符串,首位以及每个字符间添加#
test = '#'+'#'.join(s)+'#'
# 当前遍历的中心最大扩散步数,其值等于原始字符串的最长回文子串的长度
max_len = 0
for i in range(len(test)):
left = i - 1
right = i + 1
step = 0
print(test[i])
while left >= 0 and right < len(test) and test[left] == test[right]:
# print("spread",test[left],test[right])
left -= 1
right += 1
step += 1
# print(step)
if step > max_len:
max_len = step
start = (i - max_len) // 2
return s[start: start + max_len
题解三(动态规划)
特判,当s的长度为1或者0时,返回s。
初试化最长回文子串的开始索引start和最长长度max_len=1
初试化dp数组,为n*n,全部初始化为False。dp[i][j]表示]s[i−j]是否为回文串。
将dp中,所有单个字符处都是回文串,置为True。s中若相邻的字符串相同,则同样将着两个字符对应的位置置为True。即遍历s:
dp[i][i]=True,表示单个字符一定是回文串。
若i<n−1 and s[i]==s[i+1],表示相邻的字符相同。则dp[i][i+1]=True,并更新最长回文子串的开始索引start=i和长度max_len=2
此时,从长度3,开始遍历,遍历区间[3,n+1),表示所有最长子串可能的长度。因为长度为1和2的已经在上一步找完了。对于可能的长度l:
从索引0开始遍历,遍历区间[0,n−l+1),对于开始索引i。**遍历区间解释:**因为开始索引为i,长度为l,则子串右侧索引为i+l−1,为了保证不越界,i+l−1<n,则i<n−+1l。
令子串右侧索引r=i+l−1
若满足s[i]==s[r] and dp[i+1][r−1]==True。表示子串s[i+1,…,r−1]为回文且s[i]==s[r],说明s[i,…,r]也是回文。则此时,更新dp[i][r]=True,并更新最长子串开始索引start=i和长度max_len=l
返回s[start,…,start+max_len−1]
class Solution:
def longestPalindrome(self, s: str) -> str:
if(not s or len(s)==1):
return s
n=len(s)
dp=[[False]*n for _ in range(n)]
max_len=1
start=0
for i in range(n):
dp[i][i]=True
if(i<n-1 and s[i]==s[i+1]):
dp[i][i+1]=True
start=i
max_len=2
for l in range(3,n+1):
for i in range(n+1-l):
r=i+l-1
if(s[i]==s[r] and dp[i+1][r-1]):
dp[i][r]=True
start=i
max_len=l
return s[start:start+max_len]