题目
给定一个字符串 s
,找到 s
中最长的回文子串。你可以假设 s
的最大长度为 1000。
一. 中心扩张
依次遍历字符串的每个字符,然后以该字符为中心向外扩展,直到不满足回文为止,以此找出最长的子串。
js实现
/**
* @param {string} s
* @return {string}
*/
var longestPalindrome = function(s) {
var len = s.length
if (!len) {
return s
}
var result = s[0]
for (var i=0;i<len;i++) {
// 以当前字符为中心
var left = i
var right = i
while (left - 1 >= 0 && right + 1 < len) {
if (s[left-1] === s[right+1]) {
left--
right++
} else {
break
}
}
if (right + 1 - left > result.length) {
result = s.substring(left, right+1)
}
// 以当前字符与下一字符之间为中心
left = i + 1
right = i
while (left - 1 >= 0 && right + 1 < len) {
if (s[left-1] === s[right+1]) {
left--
right++
} else {
break
}
}
if (right + 1 - left > result.length) {
result = s.substring(left, right+1)
}
}
return result
};
复杂度分析
时间复杂度:O(n2)
空间复杂度:O(n)
测试结果
✔ Accepted
✔ 103/103 cases passed (80 ms)
✔ Your runtime beats 99.26 % of javascript submissions
✔ Your memory usage beats 84.53 % of javascript submissions (35.6 MB)
二. Manacher算法
在回文中,当前位置与关于对称中心的左侧位置相同,利用此性质,回文中对称位置已保存的回文半径也是当前位置的回文半径,因此,减少了判断次数,再向外扩展回文即可。
js实现
// @lc code=start
/**
* @param {string} s
* @return {string}
*/
var longestPalindrome = function(s) {
// Manacher算法
// 使用`#`填充使其长度为奇数
var slist = s.split('')
var ns = '#' + slist.join('#') + '#'
// 定义p数组
var p = new Array(ns.length).fill(0)
var id = 0 // 用于对称性的子串中心的下标
var right = 0 // id子串的最右边界下标
var reCenter = 0 // 最大回文子串中心下标
var reLen = 0 // 最大回文子串长度
// 开始遍历
for (var i=0;i<ns.length;i++) {
// 在以id为中心的回文内,当前位置和关于id对称的位置相同
if (right > i) {
p[i] = Math.min(right - i, p[2*id - i])
} else {
p[i] = 1
}
// 扩展回文子串
while (i-p[i] >= 0 && ns[i+p[i]] === ns[i-p[i]]) {
p[i]++
}
// 当i在id子串右边界之外时更新id子串
if (i + p[i] > right) {
id = i
right = i + p[i]
}
// 更新结果
if (p[i] > reLen) {
reCenter = i
reLen = p[i]
}
}
return s.substr(Math.ceil((reCenter-reLen)/2), reLen - 1)
};
复杂度分析
时间复杂度:O(n)
空间复杂度:O(n)
测试结果
✔ Accepted
✔ 103/103 cases passed (84 ms)
✔ Your runtime beats 98.84 % of javascript submissions
✔ Your memory usage beats 39.05 % of javascript submissions (37.7 MB)