Given a string s
, return the longest palindromic substring in s
.
For example:
Input: s = "babad"
Output: "bab"
or "aba"
Input: s = "cbbd"
Output: "bb"
Constraints:
1 <= s.length <= 1000
s
consist of only digits and English letters.# Longest Palindromic Substring
## Problem Description
Given a string `s`, the task is to find and return the longest palindromic substring within `s`. A palindromic substring is a sequence of characters that reads the same forwards and backward. For example, "aba" and "madam" are palindromes.
**Example 1:**
Input: `s = "babad"`
Output: `"bab"` or `"aba"`
**Example 2:**
Input: `s = "cbbd"`
Output: `"bb"`
## Brute Force Approach
A naive approach would be to generate all possible substrings of the given string and check each substring for whether it is a palindrome. We keep track of the longest palindrome found so far. This approach is simple to understand but not very efficient.
```python
def is_palindrome(s):
return s == s[::-1]
def longest_palindrome_brute_force(s):
longest = ""
for i in range(len(s)):
for j in range(i, len(s)):
substring = s[i:j+1]
if is_palindrome(substring) and len(substring) > len(longest):
longest = substring
return longest
# Example usage
s = "babad"
print(f"Brute force: Longest palindrome in '{s}' is '{longest_palindrome_brute_force(s)}'")
s = "cbbd"
print(f"Brute force: Longest palindrome in '{s}' is '{longest_palindrome_brute_force(s)}'")
A more efficient approach involves dynamic programming. We create a 2D boolean array dp[i][j]
where dp[i][j]
is True
if the substring s[i:j+1]
is a palindrome, and False
otherwise.
The base cases are:
dp[i][i] = True
for all i
(a single character is always a palindrome).dp[i][i+1] = True
if s[i] == s[i+1]
(two adjacent equal characters form a palindrome).For substrings of length 3 or more, dp[i][j] = True
if s[i] == s[j]
and dp[i+1][j-1] == True
.
def longest_palindrome_dp(s):
n = len(s)
dp = [[False] * n for _ in range(n)]
start = 0
max_len = 1
# Base case: Single character palindromes
for i in range(n):
dp[i][i] = True
# Base case: Two character palindromes
for i in range(n - 1):
if s[i] == s[i + 1]:
dp[i][i + 1] = True
start = i
max_len = 2
# Check for palindromes of length 3 or more
for k in range(3, n + 1):
for i in range(n - k + 1):
j = i + k - 1
if s[i] == s[j] and dp[i + 1][j - 1]:
dp[i][j] = True
if k > max_len:
start = i
max_len = k
return s[start:start + max_len]
# Example usage
s = "babad"
print(f"DP: Longest palindrome in '{s}' is '{longest_palindrome_dp(s)}'")
s = "cbbd"
print(f"DP: Longest palindrome in '{s}' is '{longest_palindrome_dp(s)}'")
The brute force approach iterates through all possible substrings, which takes O(n^2) time. For each substring, it checks if it's a palindrome, which takes O(n) time. Therefore, the overall time complexity is O(n^3).
The dynamic programming solution initializes a 2D array of size n x n, which takes O(n^2) space. It then iterates through the array to fill in the values, which also takes O(n^2) time. Therefore, the overall time complexity is O(n^2).
The brute force approach uses a constant amount of extra space, so the space complexity is O(1).
The dynamic programming solution uses a 2D array of size n x n to store the palindrome information. Therefore, the space complexity is O(n^2).
These edge cases are handled correctly in both the brute force and dynamic programming solutions.