给定一个字符串 s,找到 s 中最长的回文子串。你能够假设 s 的最大长度为 1000。php
示例1git
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
复制代码
示例2github
输入: "cbbd"
输出: "bb"
复制代码
示例3算法
输入: "cbad"
输出: ""
复制代码
一句话解释回文的意思是正着念和倒着念同样,如:上海自来水来自海上,山西运煤车煤运西山等,瞬间感受本身在听郭德纲相声bash
时间复杂度 O(N^3)优化
const longestPalindrome = function(s) {
if (!s) return ''
let longest = s[0]
let str, i, j, len
const isPalindrom = function (left, right) {
while (left < right && s[left] === s[right]) {
left++;
right--;
}
return left >= right;
}
for (len = 2; len <= s.length; len++) {
for (i = 0; i < s.length; i++) {
j = i + len - 1;
if (isPalindrom(i, j)) {
str = s.slice(i, j + 1);
if (longest.length < str.length) longest = str;
}
}
}
return longest
}
复制代码
时间复杂度 O(N^3) 空间复杂度 O(N^2)ui
回文字符串定位为: dp(start,end)spa
const longestPalindrome = function(s) {
let i, j, len
const isPalindrom = new Array(s.length);
for (i = 0; i < s.length; i++) {
isPalindrom[i] = new Array(s.length).fill(false);
}
let maxLen = 1, longestBegin = 0;
for (i = 0; i < s.length; i++) {
isPalindrom[i][i] = true;
if (i < s.length - 1 && s[i] === s[i + 1]) {
isPalindrom[i][i + 1] = true;
maxLen = 2;
longestBegin = i;
}
}
for (len = 3; len <= s.length; len++) {
for (i = 0; i < s.length; i++) {
j = len + i - 1;
if (s[i] === s[j] && isPalindrom[i + 1][j - 1]) {
isPalindrom[i][j] = true;
maxLen = len;
longestBegin = i;
}
}
}
return s.slice(longestBegin, longestBegin + maxLen);
}
复制代码
空间复杂度降到 O(1),只存找到的最长回文串便可。枚举轴心位置,并进行扩展。若是是回文,则轴心两边的字符应该对称相等。须要考虑到长度奇偶状况的不一样,若是是奇数长度,轴心就是一个字符;若是是偶数长度,轴心则不在字符串中code
const longestPalindrome = function(s) {
if (!s) return '';
let longest = s[0];
let expandAroundCenter = function (left, right) {
while (left >= 0 && right < s.length && s[left] === s[right]) {
left--;
right++;
}
return s.slice(left + 1, right);
}
for (let i = 0; i < s.length; i++) {
let odd = expandAroundCenter(i, i);
if (odd.length > longest.length) longest = odd;
let even = expandAroundCenter(i, i + 1);
if (longest.length < even.length) longest = even;
}
return longest;
}
复制代码
相比下降空间复杂度,下降时间复杂度要可贵多。这里有一个 O(N) 时间复杂度的算法,叫作 Manacher 算法blog
const longestPalindrome = function(s) {
s = '^#' + s.split('').join('#') + '#$';
let radius = new Array(s.length).fill(0);
let C = 0, centerIndex = 0, maxRight = 0, maxLen = 0;
for (let i = 1; i < s.length - 1; i++) {
# 计算初始回文半径, i' = 2 * C - i
radius[i] = (maxRight > i) ? Math.min(maxRight - i, radius[2 * C - i]) : 0;
# 扩展半径
while (s[i + 1 + radius[i]] && s[i - 1 - radius[i]] && s[i + 1 + radius[i]] === s[i - 1 - radius[i]]) radius[i]++;
# 更新当前搜索的最大右边界和位置
if (i + radius[i] > maxRight) {
C = i;
maxRight = i + radius[i];
}
# 更新最大回文串长度及位置
if (maxLen < radius[i]) {
maxLen = radius[i];
centerIndex = i;
}
}
return s.slice((centerIndex - maxLen), (centerIndex + maxLen + 1)).split('#').join('');
}
复制代码