这是我参与11月更文挑战的第16天,活动详情查看:2021最后一次更文挑战
题目
给你一个字符串 s,找到 s 中最长的回文子串。
示例1
输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。
示例2
输入:s = "cbbd"
输出:"bb"
示例3
输入:s = "a"
输出:"a"
思路
观察题目示例1给出的 回文子串 的定义可以发现,回文子串 指的是 从左到右 和 从右到左 是一样的字符串。
考虑两种情况,当回文子串的长度为 奇数 时,回文子串必然有一个中心,从这个中心开始向两边扩展,形成题目要求的回文;当回文子串的长度为 偶数 时,可以将最中心的两个字符视为 中心,同样向两边开始扩展,形成回文。
题目要求寻找最长回文子串,显然可以穷举出该字符的所有回文子串,找出最长的那个
解法1:遍历字符串 s,每次遍历,寻找以 i, i 和 i, i+ 1 为中心的最长回文子串,每次保留最长的子串与上一步保留的回文子串比较长度,取较长的那个
优化
显然,上述解法在遍历的过程中,寻找某个中心位置的回文子串方法可以抽离出来
最终代码
var longestPalindrome = function(s) {
if(s.length < 2) return s;
const n = s.length;
let max_left = 0, max_right = 0;
// 获取回文子串
const getPalindrome = function(left, right) {
while(left >= 0 && right < n && s.charAt(left) == s.charAt(right)) {
left--;
right++;
}
return {
left,
right
}
}
for(let i = 0; i < n; i++) {
// 寻找以 i, i 为中心的回文子串
let palindromeOne = getPalindrome(i, i);
if(palindromeOne.right - palindromeOne.left > max_right - max_left) {
max_left = palindromeOne.left;
max_right = palindromeOne.right;
}
// 寻找以 i, i + 1 为中心的回文子串
let palindromeTwo = getPalindrome(i, i + 1);
if(palindromeTwo.right - palindromeTwo.left > max_right - max_left) {
max_left = palindromeTwo.left;
max_right = palindromeTwo.right;
}
}
return s.substring(max_left + 1, max_right);
}
小结
上述的解法在官方称之为中心扩展,算法的时间复杂度为 O(n^2),其中 n 是字符串的长度。长度为 1 和 2 的回文中心分别有 n 和 n-1 个,每个回文中心最多会向外扩展 O(n) 次,空间复杂度为 O(1),也算是一种暴力解法吧。官方还有 动态规划、Manacher 算法等解法,可以去看看。
算法,适合自己的才是最好的😊
LeetCode 👉 HOT 100 👉 最长回文子串 - 中等题 ✅
LeetCode 👉 HOT 100 👉 无重复字符的最长子串 - 中等题 ✅