「这是我参与11月更文挑战的5天,活动详情查看:2021最后一次更文挑战」。
前言
一直都计划学习数据结构与基本算法,但是平时都看一阵停一阵。现在决心坚持下去,我准备从LeetCode的HOT100开始,每天完成1~2道习题,希望通过这种方式养成持续学习的习惯。因为我是做iOS开发的,主要是用Objective-C语言,最近也在学习Swift,所以本系列的题解都将使用swift语言完成,本文更新的是LeetCode中HOT100的第5题005最长回文子串。
题目
给你一个字符串 s,找到 s 中最长的回文子串。
示例 1:
输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。
示例 2:
输入:s = "cbbd"
输出:"bb"
示例 3:
输入:s = "a"
输出:"a"
示例 4:
输入:s = "ac"
输出:"a"
提示:
1 <= s.length <= 1000
s 仅由数字和英文字母(大写和/或小写)组成
分析
本题的目的非常简单,就是求一个字符串中最长的回文子串。回文串(palindromic string)是指这个字符串无论从左读还是从右读,所读的顺序是一样的;简而言之,回文串是左右对称的。类似下面的都是回文串。
abcdcba
abba
aaa
暴力解法
要达到这个目的,我们最容易想到的方法就是暴力解法,就是按照子串长度从大到小的顺序依次判断每一个子串是否是回文串,如果是,则返回该回文串即可, 如果不是,继续判断下一个。暴力解法的问题在于复杂度太高,很短的字符串可以用这种方法,如果子串长度稍微大一些,容易造成超时。
中心扩散法
因为回文串的特点是关于中心点左右对称的,所以一种简化的方法就是先确定中心点,然后求该中心点对应的最大回文串,这种方法成为中心扩散法。本文也是采用这种方法进行题解的,具体的解题思路如下:
从每一个位置出发,向两边扩散即可。遇到不是回文的时候结束。举个例子,str=acdbbdaa 我们需要寻找从第一个 b(位置为 3)出发最长回文串为多少。怎么寻找?
1、首先往左寻找与当期位置相同的字符,直到遇到不相等为止。
2、然后往右寻找与当期位置相同的字符,直到遇到不相等为止。
3、最后左右双向扩散,直到左和右不相等。
下图就是中心扩散法的图解:
动态规划法
中心扩散的方法,其实做了很多重复计算。动态规划就是为了减少重复计算的问题。动态规划听起来很高大上。其实说白了就是空间换时间,将计算结果暂存起来,避免重复计算。
我们用一个 bool类型的二维数组 表示字符串从 l 到 r 这段是否为回文。试想如果 ,我们要判断 是否为回文。只需要判断字符串在(l-1)和(r+1)两个位置是否为相同的字符,这样就可以减少了很多重复计算。
动态规划关键是找到初始状态和状态转移方程,在本题中初始状态和状态转移方程如下:\
初始状态:l=r 时,此时 $dp[l][r]=true$。
状态转移方程:dp[l][r]=true 并且(l-1)和(r+1)两个位置为相同的字符,此时 dp[l-1][r+1]=true。
动态规划的方法我没有具体去写,读者感兴趣的话可以自己尝试写一下。
题解
class Solution {
func longestPalindrome(_ s: String) -> String {
let chArr = [Character](s)
var maxLen = 0
var maxStart = 0
for (currIndex, ch) in chArr.enumerated() {
var left = currIndex - 1
var right = currIndex + 1
var curLen = 1
//往左扩散,与中心点相同的字符
while left >= 0 && chArr[left] == ch {
left -= 1
curLen += 1
}
//往右扩散,与中心点相同的字符
while right < chArr.count && chArr[right] == ch {
right += 1
curLen += 1
}
//左右同时扩散
while left >= 0 && right < chArr.count && chArr[left] == chArr[right] {
left -= 1
right += 1
curLen += 2
}
//更新当前最长回文子串的最大长度和开始位置
if curLen > maxLen {
maxLen = curLen
maxStart = left + 1
}
}
//获取最长回文子串
let ans = String(s[s.index(s.startIndex, offsetBy: maxStart)..<s.index(s.startIndex, offsetBy: (maxStart+maxLen))])
return ans
}
}