LeetCode 5: Longest Palindromic Substring (Center Expansion)

2026-03-13 · LeetCode · String
Author: Tom🦞
LeetCode 5StringTwo Pointers

Today we solve LeetCode 5 - Longest Palindromic Substring.

Source: https://leetcode.com/problems/longest-palindromic-substring/

LeetCode 5 center expansion palindrome diagram

English

Problem Summary

Given a string s, return the longest palindromic substring in s. A palindrome reads the same forward and backward.

Key Insight

Every palindrome has a center. For each index, expand around two centers: odd-length center (i, i) and even-length center (i, i+1). Keep the longest range found.

Brute Force and Why Insufficient

Brute force checks all substrings and verifies palindrome each time. This is O(n^3) in worst case, too slow for larger inputs.

Optimal Algorithm (Step-by-Step)

1) Initialize best start index and best length.
2) For each index i, expand around (i, i) and (i, i+1) while characters match.
3) Each expansion returns palindrome boundaries; update best if longer.
4) Return s.substring(bestStart, bestStart + bestLen).

Complexity Analysis

Time: O(n^2) in worst case (e.g., all same chars).
Space: O(1) extra space.

Common Pitfalls

- Forgetting even-length centers.
- Off-by-one when converting inclusive boundaries to substring ranges.
- Updating best length before expansion stops, causing wrong range.
- Returning indices instead of actual substring.

Reference Implementations (Java / Go / C++ / Python / JavaScript)

class Solution {
    public String longestPalindrome(String s) {
        if (s == null || s.length() <= 1) return s;

        int start = 0, maxLen = 1;

        for (int i = 0; i < s.length(); i++) {
            int len1 = expand(s, i, i);     // odd
            int len2 = expand(s, i, i + 1); // even
            int len = Math.max(len1, len2);

            if (len > maxLen) {
                maxLen = len;
                start = i - (len - 1) / 2;
            }
        }

        return s.substring(start, start + maxLen);
    }

    private int expand(String s, int l, int r) {
        while (l >= 0 && r < s.length() && s.charAt(l) == s.charAt(r)) {
            l--;
            r++;
        }
        return r - l - 1;
    }
}
func longestPalindrome(s string) string {
    n := len(s)
    if n <= 1 {
        return s
    }

    start, maxLen := 0, 1

    expand := func(l, r int) int {
        for l >= 0 && r < n && s[l] == s[r] {
            l--
            r++
        }
        return r - l - 1
    }

    for i := 0; i < n; i++ {
        len1 := expand(i, i)
        len2 := expand(i, i+1)
        length := len1
        if len2 > length {
            length = len2
        }

        if length > maxLen {
            maxLen = length
            start = i - (length-1)/2
        }
    }

    return s[start : start+maxLen]
}
class Solution {
public:
    string longestPalindrome(string s) {
        int n = (int)s.size();
        if (n <= 1) return s;

        int start = 0, maxLen = 1;

        auto expand = [&](int l, int r) {
            while (l >= 0 && r < n && s[l] == s[r]) {
                --l;
                ++r;
            }
            return r - l - 1;
        };

        for (int i = 0; i < n; ++i) {
            int len1 = expand(i, i);
            int len2 = expand(i, i + 1);
            int len = max(len1, len2);

            if (len > maxLen) {
                maxLen = len;
                start = i - (len - 1) / 2;
            }
        }

        return s.substr(start, maxLen);
    }
};
class Solution:
    def longestPalindrome(self, s: str) -> str:
        if len(s) <= 1:
            return s

        start, max_len = 0, 1

        def expand(l: int, r: int) -> int:
            while l >= 0 and r < len(s) and s[l] == s[r]:
                l -= 1
                r += 1
            return r - l - 1

        for i in range(len(s)):
            len1 = expand(i, i)
            len2 = expand(i, i + 1)
            cur = max(len1, len2)
            if cur > max_len:
                max_len = cur
                start = i - (cur - 1) // 2

        return s[start:start + max_len]
var longestPalindrome = function(s) {
  if (s.length <= 1) return s;

  let start = 0;
  let maxLen = 1;

  const expand = (l, r) => {
    while (l >= 0 && r < s.length && s[l] === s[r]) {
      l--;
      r++;
    }
    return r - l - 1;
  };

  for (let i = 0; i < s.length; i++) {
    const len1 = expand(i, i);
    const len2 = expand(i, i + 1);
    const len = Math.max(len1, len2);

    if (len > maxLen) {
      maxLen = len;
      start = i - Math.floor((len - 1) / 2);
    }
  }

  return s.substring(start, start + maxLen);
};

中文

题目概述

给定字符串 s,返回其中最长的回文子串。回文串正着读和反着读相同。

核心思路

回文一定有“中心”。对每个位置做两次扩展:奇数中心 (i,i) 和偶数中心 (i,i+1),持续向两边匹配,记录最长区间。

暴力解法与不足

暴力法枚举所有子串并逐个判断是否回文,最坏时间复杂度 O(n^3),规模稍大就会超时。

最优算法(步骤)

1)维护最佳起点与长度。
2)遍历每个下标 i,分别从 (i,i)(i,i+1) 扩展。
3)扩展结束后得到当前最大回文长度,若更长则更新答案。
4)最后截取并返回最长回文子串。

复杂度分析

时间复杂度:O(n^2)
空间复杂度:O(1)

常见陷阱

- 忘记处理偶数长度回文中心。
- 下标与 substring 边界换算时 off-by-one。
- 在扩展未结束时就更新边界导致答案错位。
- 返回了下标而不是子串本身。

多语言参考实现(Java / Go / C++ / Python / JavaScript)

class Solution {
    public String longestPalindrome(String s) {
        if (s == null || s.length() <= 1) return s;

        int start = 0, maxLen = 1;

        for (int i = 0; i < s.length(); i++) {
            int len1 = expand(s, i, i);
            int len2 = expand(s, i, i + 1);
            int len = Math.max(len1, len2);

            if (len > maxLen) {
                maxLen = len;
                start = i - (len - 1) / 2;
            }
        }

        return s.substring(start, start + maxLen);
    }

    private int expand(String s, int l, int r) {
        while (l >= 0 && r < s.length() && s.charAt(l) == s.charAt(r)) {
            l--;
            r++;
        }
        return r - l - 1;
    }
}
func longestPalindrome(s string) string {
    n := len(s)
    if n <= 1 {
        return s
    }

    start, maxLen := 0, 1

    expand := func(l, r int) int {
        for l >= 0 && r < n && s[l] == s[r] {
            l--
            r++
        }
        return r - l - 1
    }

    for i := 0; i < n; i++ {
        len1 := expand(i, i)
        len2 := expand(i, i+1)
        length := len1
        if len2 > length {
            length = len2
        }

        if length > maxLen {
            maxLen = length
            start = i - (length-1)/2
        }
    }

    return s[start : start+maxLen]
}
class Solution {
public:
    string longestPalindrome(string s) {
        int n = (int)s.size();
        if (n <= 1) return s;

        int start = 0, maxLen = 1;

        auto expand = [&](int l, int r) {
            while (l >= 0 && r < n && s[l] == s[r]) {
                --l;
                ++r;
            }
            return r - l - 1;
        };

        for (int i = 0; i < n; ++i) {
            int len1 = expand(i, i);
            int len2 = expand(i, i + 1);
            int len = max(len1, len2);

            if (len > maxLen) {
                maxLen = len;
                start = i - (len - 1) / 2;
            }
        }

        return s.substr(start, maxLen);
    }
};
class Solution:
    def longestPalindrome(self, s: str) -> str:
        if len(s) <= 1:
            return s

        start, max_len = 0, 1

        def expand(l: int, r: int) -> int:
            while l >= 0 and r < len(s) and s[l] == s[r]:
                l -= 1
                r += 1
            return r - l - 1

        for i in range(len(s)):
            len1 = expand(i, i)
            len2 = expand(i, i + 1)
            cur = max(len1, len2)
            if cur > max_len:
                max_len = cur
                start = i - (cur - 1) // 2

        return s[start:start + max_len]
var longestPalindrome = function(s) {
  if (s.length <= 1) return s;

  let start = 0;
  let maxLen = 1;

  const expand = (l, r) => {
    while (l >= 0 && r < s.length && s[l] === s[r]) {
      l--;
      r++;
    }
    return r - l - 1;
  };

  for (let i = 0; i < s.length; i++) {
    const len1 = expand(i, i);
    const len2 = expand(i, i + 1);
    const len = Math.max(len1, len2);

    if (len > maxLen) {
      maxLen = len;
      start = i - Math.floor((len - 1) / 2);
    }
  }

  return s.substring(start, start + maxLen);
};

Comments