Given a string s, find the longest substring which is a palindrome. If there are multiple answers, then find the first appearing substring.
Examples:
Input: s = "forgeeksskeegfor"
Output: "geeksskeeg"
Explanation: The longest substring that reads the same forward and backward is "geeksskeeg". Other palindromes like "kssk" or "eeksskee" are shorter.Input: s = "Geeks"
Output: "ee"
Explanation: The substring "ee" is the longest palindromic part in "Geeks". All others are shorter single characters.Input: s = "abc"
Output: "a"
Explanation: No multi-letter palindromes exist. So the first character "a" is returned as the longest palindromic substring.
Table of Content
[Naive Approach] Generating all Substrings - O(n3) time and O(1) space
Generate all possible substrings of the given string. For each substring, check if it is a palindrome. If it is, update the result if its length is greater than the longest palindrome found so far.
#include <iostream>
using namespace std;
// function to check if a substring
// s[low..high] is a palindrome
bool checkPal(string &s, int low, int high) {
while (low < high) {
if (s[low] != s[high])
return false;
low++;
high--;
}
return true;
}
// function to find the longest palindrome substring
string getLongestPal(string& s) {
int n = s.size();
// all substrings of length 1 are palindromes
int maxLen = 1, start = 0;
// nested loop to mark start and end index
for (int i = 0; i < n; i++) {
for (int j = i; j < n; j++) {
// check if the current substring is
// a palindrome
if (checkPal(s, i, j) && (j - i + 1) > maxLen) {
start = i;
maxLen = j - i + 1;
}
}
}
return s.substr(start, maxLen);
}
int main() {
string s = "forgeeksskeegfor";
cout << getLongestPal(s) << endl;
return 0;
}
class GfG {
// function to check if a substring
// s[low..high] is a palindrome
static boolean checkPal(String s, int low, int high) {
while (low < high) {
if (s.charAt(low) != s.charAt(high))
return false;
low++;
high--;
}
return true;
}
// function to find the longest palindrome substring
static String getLongestPal(String s) {
int n = s.length();
// all substrings of length 1 are palindromes
int maxLen = 1, start = 0;
// nested loop to mark start and end index
for (int i = 0; i < n; i++) {
for (int j = i; j < n; j++) {
// check if the current substring is
// a palindrome
if (checkPal(s, i, j) && (j - i + 1) > maxLen) {
start = i;
maxLen = j - i + 1;
}
}
}
return s.substring(start, start + maxLen);
}
public static void main(String[] args) {
String s = "forgeeksskeegfor";
System.out.println(getLongestPal(s));
}
}
# function to check if a substring
# s[low..high] is a palindrome
def checkPal(str, low, high):
while low < high:
if str[low] != str[high]:
return False
low += 1
high -= 1
return True
# function to find the longest palindrome substring
def getLongestPal(s):
n = len(s)
# all substrings of length 1 are palindromes
maxLen = 1
start = 0
# nested loop to mark start and end index
for i in range(n):
for j in range(i, n):
# check if the current substring is
# a palindrome
if checkPal(s, i, j) and (j - i + 1) > maxLen:
start = i
maxLen = j - i + 1
return s[start:start + maxLen]
if __name__ == "__main__":
s = "forgeeksskeegfor"
print(getLongestPal(s))
using System;
class GfG {
// function to check if a substring
// s[low..high] is a palindrome
static bool checkPal(string s, int low, int high) {
while (low < high) {
if (s[low] != s[high])
return false;
low++;
high--;
}
return true;
}
// function to find the longest palindrome substring
static string getLongestPal(string s) {
int n = s.Length;
// all substrings of length 1 are palindromes
int maxLen = 1, start = 0;
// nested loop to mark start and end index
for (int i = 0; i < n; i++) {
for (int j = i; j < n; j++) {
// check if the current substring is
// a palindrome
if (checkPal(s, i, j) && (j - i + 1) > maxLen) {
start = i;
maxLen = j - i + 1;
}
}
}
return s.Substring(start, maxLen);
}
static void Main(string[] args) {
string s = "forgeeksskeegfor";
Console.WriteLine(getLongestPal(s));
}
}
// function to check if a substring
// s[low..high] is a palindrome
function checkPal(s, low, high) {
while (low < high) {
if (s[low] !== s[high])
return false;
low++;
high--;
}
return true;
}
// function to find the longest palindrome substring
function getLongestPal(s) {
const n = s.length;
// all substrings of length 1 are palindromes
let maxLen = 1, start = 0;
// nested loop to mark start and end index
for (let i = 0; i < n; i++) {
for (let j = i; j < n; j++) {
// check if the current substring is
// a palindrome
if (checkPal(s, i, j) && (j - i + 1) > maxLen) {
start = i;
maxLen = j - i + 1;
}
}
}
return s.substring(start, start + maxLen);
}
// Driver Code
const s = "forgeeksskeegfor";
console.log(getLongestPal(s));
Output
geeksskeeg
[Better Approach - 1] Dynamic Programming - O(n2) time and O(n2) space
The idea is to use Dynamic Programming to store the status of smaller substrings and use these results to check if a longer substring forms a palindrome.
- The main idea behind the approach is that if we know the status (i.e., palindrome or not) of the substring ranging [i, j], we can find the status of the substring ranging [i-1, j+1] by only matching the character str[i-1] and str[j+1].
- If the substring from i to j is not a palindrome, then the substring from i-1 to j+1 will also not be a palindrome. Otherwise, it will be a palindrome only if str[i-1] and str[j+1] are the same.
- Base on this fact, we can create a 2D table (say table[][] which stores status of substring str[i . . . j] ), and check for substrings with length from 1 to n. For each length find all the substrings starting from each character i and find if it is a palindrome or not using the above idea. The longest length for which a palindrome formed will be the required answer.
Note: Refer to Longest Palindromic Substring using Dynamic Programming for detailed approach.
#include <iostream>
#include <vector>
using namespace std;
string getLongestPal(string s) {
int n = s.size();
vector<vector<bool>> dp(n, vector<bool>(n, false));
// dp[i][j] if the substring from [i to j]
// is a palindrome or not
int start = 0, maxLen = 1;
// all substrings of length 1 are palindromes
for (int i = 0; i < n; ++i) dp[i][i] = true;
// check for substrings of length 2
for (int i = 0; i < n - 1; ++i) {
if (s[i] == s[i+1]) {
dp[i][i+1] = true;
if(maxLen==1){
start = i;
maxLen = 2;
}
}
}
// check for substrings of length 3 and more
for (int len = 3; len <= n; ++len) {
for (int i = 0; i <= n - len; ++i) {
int j = i + len - 1;
// if s[i] == s[j] then check for
// i [i+1 --- j-1] j
if (s[i] == s[j] && dp[i+1][j-1]) {
dp[i][j] = true;
if(len>maxLen){
start = i;
maxLen = len;
}
}
}
}
return s.substr(start, maxLen);
}
int main() {
string s = "forgeeksskeegfor";
cout << getLongestPal(s) << endl;
}
class GfG {
public static String getLongestPal(String s) {
int n = s.length();
boolean[][] dp = new boolean[n][n];
// dp[i][j] if the substring from [i to j]
// is a palindrome or not
int start = 0, maxLen = 1;
// all substrings of length 1 are palindromes
for (int i = 0; i < n; ++i) dp[i][i] = true;
// check for substrings of length 2
for (int i = 0; i < n - 1; ++i) {
if (s.charAt(i) == s.charAt(i + 1)) {
dp[i][i + 1] = true;
if(maxLen == 1){
start = i;
maxLen = 2;
}
}
}
// check for substrings of length 3 and more
for (int len = 3; len <= n; ++len) {
for (int i = 0; i <= n - len; ++i) {
int j = i + len - 1;
// if s[i] == s[j] then check for
// i [i+1 --- j-1] j
if (s.charAt(i) == s.charAt(j) && dp[i + 1][j - 1]) {
dp[i][j] = true;
if(len > maxLen){
start = i;
maxLen = len;
}
}
}
}
return s.substring(start, start + maxLen);
}
public static void main(String[] args) {
String s = "forgeeksskeegfor";
System.out.println(getLongestPal(s));
}
}
def getLongestPal(s):
n = len(s)
dp = [[False] * n for _ in range(n)]
# dp[i][j] if the substring from [i to j] is a palindrome or not
start = 0
maxLen = 1
# all substrings of length 1 are palindromes
for i in range(n):
dp[i][i] = True
# check for substrings of length 2
for i in range(n - 1):
if s[i] == s[i + 1]:
dp[i][i + 1] = True
if maxLen == 1:
start = i
maxLen = 2
# check for substrings of length 3 and more
for length in range(3, n + 1):
for i in range(n - length + 1):
j = i + length - 1
# if s[i] == s[j] then check for [i+1 .. j-1]
if s[i] == s[j] and dp[i + 1][j - 1]:
dp[i][j] = True
if length > maxLen:
start = i
maxLen = length
return s[start:start + maxLen]
if __name__ == "__main__":
s = "forgeeksskeegfor"
print(getLongestPal(s))
using System;
class GfG {
static string getLongestPal(string s) {
int n = s.Length;
bool[,] dp = new bool[n, n];
// dp[i][j] if the substring from [i to j]
// is a palindrome or not
int start = 0, maxLen = 1;
// all substrings of length 1 are palindromes
for (int i = 0; i < n; ++i) dp[i, i] = true;
// check for substrings of length 2
for (int i = 0; i < n - 1; ++i) {
if (s[i] == s[i + 1]) {
dp[i, i + 1] = true;
if (maxLen == 1) {
start = i;
maxLen = 2;
}
}
}
// check for substrings of length 3 and more
for (int len = 3; len <= n; ++len) {
for (int i = 0; i <= n - len; ++i) {
int j = i + len - 1;
// if s[i] == s[j] then check for
// i [i+1 --- j-1] j
if (s[i] == s[j] && dp[i + 1, j - 1]) {
dp[i, j] = true;
if (len > maxLen) {
start = i;
maxLen = len;
}
}
}
}
return s.Substring(start, maxLen);
}
public static void Main(string[] args) {
string s = "forgeeksskeegfor";
Console.WriteLine(getLongestPal(s));
}
}
function getLongestPal(s) {
let n = s.length;
const dp = Array.from({ length: n }, () => new Uint8Array(n));
// dp[i][j] if the substring from [i to j]
// is a palindrome or not
let start = 0, maxLen = 1;
// all substrings of length 1 are palindromes
for (let i = 0; i < n; ++i) dp[i][i] = true;
// check for substrings of length 2
for (let i = 0; i < n - 1; ++i) {
if (s[i] === s[i + 1]) {
dp[i][i + 1] = true;
if (maxLen === 1) {
start = i;
maxLen = 2;
}
}
}
// check for substrings of length 3 and more
for (let len = 3; len <= n; ++len) {
for (let i = 0; i <= n - len; ++i) {
let j = i + len - 1;
// if s[i] == s[j] then check for
// i [i+1 --- j-1] j
if (s[i] === s[j] && dp[i + 1][j - 1]) {
dp[i][j] = true;
if (len > maxLen) {
start = i;
maxLen = len;
}
}
}
}
return s.substring(start, start + maxLen);
}
// Driver Code
let s = "forgeeksskeegfor";
console.log(getLongestPal(s));
Output
geeksskeeg
[Better Approach - 2] Expansion from Center - O(n2) time and O(1) space
The idea is to traverse each character in the string and treat it as a potential center of a palindrome, trying to expand around it in both directions while checking if the expanded substring remains a palindrome.
- For each position, we check for both odd-length palindromes (where the current character is the center) and even-length palindromes (where the current character and the next character together form the center).
- As we expand outward from each center, we keep track of the start position and length of the longest palindrome found so far, updating these whenever we find a longer palindrome.
Step-by-step approach:
- Iterate through each character in the string, treating it as the center of a potential palindrome.
- For each center, expand in two ways: one for odd-length palindromes (center at index i) and one for even-length palindromes (center between indices i and i+1)
- Use two pointers low and high to track the left and right boundaries of the current palindrome.
- While low and high are in bounds and s[low] == s[high], expand outward.
- If the current palindrome length (high - low + 1) is greater than the previous maximum, update the starting index and max length.
- After checking all centers, return the substring starting at start with length maxLen.
#include <iostream>
using namespace std;
string getLongestPal(string &s) {
int n = s.length();
int start = 0, maxLen = 1;
for (int i = 0; i < n; i++) {
// this runs two times for both odd and even
// length palindromes.
// j = 0 means odd and j = 1 means even length
for (int j = 0; j <= 1; j++) {
int low = i;
int high = i + j;
// expand substring while it is a palindrome
// and in bounds
while (low >= 0 && high < n && s[low] == s[high])
{
int currLen = high - low + 1;
if (currLen > maxLen) {
start = low;
maxLen = currLen;
}
low--;
high++;
}
}
}
return s.substr(start, maxLen);
}
int main() {
string s = "forgeeksskeegfor";
cout << getLongestPal(s) << endl;
return 0;
}
class GfG {
static String getLongestPal(String s) {
int n = s.length();
int start = 0, maxLen = 1;
for (int i = 0; i < n; i++) {
// this runs two times for both odd and even
// length palindromes.
// j = 0 means odd and j = 1 means even length
for (int j = 0; j <= 1; j++) {
int low = i;
int high = i + j;
// expand substring while it is a palindrome
// and in bounds
while (low >= 0 && high < n && s.charAt(low) == s.charAt(high))
{
int currLen = high - low + 1;
if (currLen > maxLen) {
start = low;
maxLen = currLen;
}
low--;
high++;
}
}
}
return s.substring(start, start + maxLen);
}
public static void main(String[] args) {
String s = "forgeeksskeegfor";
System.out.println(getLongestPal(s));
}
}
def getLongestPal(s):
n = len(s)
start, maxLen = 0, 1
for i in range(n):
# this runs two times for both odd and even
# length palindromes.
# j = 0 means odd and j = 1 means even length
for j in range(2):
low, high = i, i + j
# expand substring while it is a palindrome
# and in bounds
while low >= 0 and high < n and s[low] == s[high]:
currLen = high - low + 1
if currLen > maxLen:
start = low
maxLen = currLen
low -= 1
high += 1
return s[start:start + maxLen]
if __name__ == "__main__":
s = "forgeeksskeegfor"
print(getLongestPal(s))
using System;
class GfG {
static string getLongestPal(string s) {
int n = s.Length;
int start = 0, maxLen = 1;
for (int i = 0; i < n; i++) {
// this runs two times for both odd and even
// length palindromes.
// j = 0 means odd and j = 1 means even length
for (int j = 0; j <= 1; j++) {
int low = i;
int high = i + j;
// expand substring while it is a palindrome
// and in bounds
while (low >= 0 && high < n && s[low] == s[high])
{
int currLen = high - low + 1;
if (currLen > maxLen) {
start = low;
maxLen = currLen;
}
low--;
high++;
}
}
}
return s.Substring(start, maxLen);
}
static void Main(string[] args) {
string s = "forgeeksskeegfor";
Console.WriteLine(getLongestPal(s));
}
}
function getLongestPal(s) {
const n = s.length;
let start = 0, maxLen = 1;
for (let i = 0; i < n; i++) {
// this runs two times for both odd and even
// length palindromes.
// j = 0 means odd and j = 1 means even length
for (let j = 0; j <= 1; j++) {
let low = i;
let high = i + j;
// expand substring while it is a palindrome
// and in bounds
while (low >= 0 && high < n && s[low] === s[high])
{
const currLen = high - low + 1;
if (currLen > maxLen) {
start = low;
maxLen = currLen;
}
low--;
high++;
}
}
}
return s.substring(start, start + maxLen);
}
// Driver Code
const s = "forgeeksskeegfor";
console.log(getLongestPal(s));
Output
geeksskeeg
[Expected Approach] Manacher’s Algorithm - O(n) time and O(n) space
The idea is to use Manacher’s algorithm, which transforms the input string by inserting separators (#) and sentinels to handle both even and odd-length palindromes uniformly.
- For each position in the transformed string, we expand the longest possible palindrome centered there using mirror symmetry and previously computed values.
- This expansion is bounded efficiently using the rightmost known palindrome range to avoid redundant checks.
- Whenever a longer palindrome is found, we update its length and starting index in the original string.
#include <iostream>
#include <string>
#include <vector>
using namespace std;
string getLongestPal(string &s)
{
if (s.empty())
return "";
// Transform the string using separators
// and sentinels.
string ms = "@";
for (char c : s)
{
ms += "#" + string(1, c);
}
ms += "#$";
int m = ms.size();
vector<int> p(m, 0);
int l = 0, r = 0;
// Manacher's algorithm
for (int i = 1; i < m - 1; ++i)
{
// Use the mirror position
if (i < r)
{
int mirror = l + r - i;
p[i] = min(r - i, p[mirror]);
}
// Expand around the current center
while (ms[i + 1 + p[i]] == ms[i - 1 - p[i]])
{
++p[i];
}
// Update the current palindrome range
if (i + p[i] > r)
{
l = i - p[i];
r = i + p[i];
}
}
int maxLen = 1;
int start = 0;
// Find the longest palindrome
for (int i = 1; i < m - 1; ++i)
{
if (p[i] > maxLen)
{
maxLen = p[i];
start = (i - p[i]) / 2;
}
}
return s.substr(start, maxLen);
}
int main()
{
string s = "forgeeksskeegfor";
cout << getLongestPal(s) << endl;
return 0;
}
import java.util.Arrays;
public class GFG {
public static String getLongestPal(String s)
{
if (s.isEmpty())
return "";
// Transform the string using separators
// and sentinels.
StringBuilder ms = new StringBuilder("@");
for (char c : s.toCharArray()) {
ms.append("#").append(c);
}
ms.append("#$");
int m = ms.length();
int[] p = new int[m];
int l = 0, r = 0;
// Manacher's algorithm
for (int i = 1; i < m - 1; ++i) {
// Use the mirror position
if (i < r) {
int mirror = l + r - i;
p[i] = Math.min(r - i, p[mirror]);
}
// Expand around the current center
while (ms.charAt(i + 1 + p[i])
== ms.charAt(i - 1 - p[i])) {
++p[i];
}
// Update the current palindrome range
if (i + p[i] > r) {
l = i - p[i];
r = i + p[i];
}
}
int maxLen = 1;
int start = 0;
// Find the longest palindrome
for (int i = 1; i < m - 1; ++i) {
if (p[i] > maxLen) {
maxLen = p[i];
start = (i - p[i]) / 2;
}
}
return s.substring(start, start + maxLen);
}
public static void main(String[] args)
{
String s = "forgeeksskeegfor";
System.out.println(getLongestPal(s));
}
}
def getLongestPal(s):
if not s:
return ""
# Transform the string using separators
# and sentinels.
ms = "@"
for c in s:
ms += "#" + c
ms += "#$"
m = len(ms)
p = [0] * m
l, r = 0, 0
# Manacher's algorithm
for i in range(1, m - 1):
# Use the mirror position
if i < r:
mirror = l + r - i
p[i] = min(r - i, p[mirror])
# Expand around the current center
while i - 1 - p[i] >= 0 and i + 1 + p[i] < m and ms[i - 1 - p[i]] == ms[i + 1 + p[i]]:
p[i] += 1
# Update the current palindrome range
if i + p[i] > r:
l = i - p[i]
r = i + p[i]
maxLen = 1
start = 0
# Find the longest palindrome
for i in range(1, m - 1):
if p[i] > maxLen:
maxLen = p[i]
start = (i - p[i]) // 2
return s[start:start + maxLen]
if __name__ == '__main__':
s = "forgeeksskeegfor"
print(getLongestPal(s))
using System;
class GFG {
static string getLongestPal(string s)
{
if (string.IsNullOrEmpty(s))
return "";
// Transform the string using separators
// and sentinels.
string ms = "@";
foreach(char c in s) { ms += "#" + c; }
ms += "#$";
int m = ms.Length;
int[] p = new int[m];
int l = 0, r = 0;
// Manacher's algorithm
for (int i = 1; i < m - 1; ++i) {
// Use the mirror position
if (i < r) {
int mirror = l + r - i;
p[i] = Math.Min(r - i, p[mirror]);
}
// Expand around the current center
while (ms[i + 1 + p[i]] == ms[i - 1 - p[i]]) {
++p[i];
}
// Update the current palindrome range
if (i + p[i] > r) {
l = i - p[i];
r = i + p[i];
}
}
int maxLen = 1;
int start = 0;
// Find the longest palindrome
for (int i = 1; i < m - 1; ++i) {
if (p[i] > maxLen) {
maxLen = p[i];
start = (i - p[i]) / 2;
}
}
return s.Substring(start, maxLen);
}
static void Main()
{
string s = "forgeeksskeegfor";
Console.WriteLine(getLongestPal(s));
}
}
function getLongestPal(s)
{
if (s.length === 0)
return "";
// Transform the string using separators
// and sentinels.
let ms = "@";
for (let c of s) {
ms += "#" + c;
}
ms += "#$";
let m = ms.length;
let p = new Array(m).fill(0);
let l = 0, r = 0;
// Manacher's algorithm
for (let i = 1; i < m - 1; ++i) {
// Use the mirror position
if (i < r) {
let mirror = l + r - i;
p[i] = Math.min(r - i, p[mirror]);
}
// Expand around the current center
while (ms[i + 1 + p[i]] === ms[i - 1 - p[i]]) {
++p[i];
}
// Update the current palindrome range
if (i + p[i] > r) {
l = i - p[i];
r = i + p[i];
}
}
let maxLen = 1;
let start = 0;
// Find the longest palindrome
for (let i = 1; i < m - 1; ++i) {
if (p[i] > maxLen) {
maxLen = p[i];
start = (i - p[i]) / 2;
}
}
return s.substring(start, start + maxLen);
}
// Driver Code
console.log(getLongestPal("forgeeksskeegfor"));
Output
geeksskeeg