Longest Palindromic Substring using Palindromic Tree | Set 3
Last Updated :
11 Jul, 2025
Given a string, find the longest substring which is a palindrome. For example, if the given string is “forgeeksskeegfor”, the output should be “geeksskeeg”.
Prerequisite : Palindromic Tree | Longest Palindromic Substring
Structure of Palindromic Tree :
The palindromic Tree’s actual structure is close to the directed graph. It is actually a merged structure of two Trees that share some common nodes(see the figure below for a better understanding). Tree nodes store palindromic substrings of a given string by storing their indices. This tree consists of two types of edges:
- Insertion edge (weighted edge)
- Maximum Palindromic Suffix (un-weighted)
Insertion Edge :
Insertion edge from a node u to v with some weight x means that the node v is formed by inserting x at the front and end of the string at u. As 'u' is already a palindrome, hence the resulting string at node v will also be a palindrome. x will be a single character for every edge. Therefore, a node can have a max of 26 insertion edges (considering lower letter string).
Maximum Palindromic Suffix Edge :
As the name itself indicates that for a node this edge will point to its Maximum Palindromic Suffix String node. We will not be considering the complete string itself as the Maximum Palindromic Suffix as this will make no sense(self-loops). For simplicity purposes, we will call it Suffix edge(by which we mean maximum suffix except for the complete string). It is quite obvious that every node will have only 1 Suffix Edge as we will not store duplicate strings in the tree. We will create all the palindromic substrings and then return the last one we got since that would be the longest palindromic substring so far. Since Palindromic Tree stores the palindromes in order of arrival of a certain character, the Longest will always be at the last index of our tree array
Below is the implementation of the above approach :
C++
// C++ program to demonstrate working of
// palindromic tree
#include "bits/stdc++.h"
using namespace std;
#define MAXN 1000
struct Node {
// store start and end indexes of current
// Node inclusively
int start, end;
// stores length of substring
int length;
// stores insertion Node for all characters a-z
int insertEdg[26];
// stores the Maximum Palindromic Suffix Node for
// the current Node
int suffixEdg;
};
// two special dummy Nodes as explained above
Node root1, root2;
// stores Node information for constant time access
Node tree[MAXN];
// Keeps track the current Node while insertion
int currNode;
string s;
int ptr;
void insert(int idx)
{
// STEP 1//
/* search for Node X such that s[idx] X S[idx]
is maximum palindrome ending at position idx
iterate down the suffix link of currNode to
find X */
int tmp = currNode;
while (true) {
int curLength = tree[tmp].length;
if (idx - curLength >= 1
and s[idx] == s[idx - curLength - 1])
break;
tmp = tree[tmp].suffixEdg;
}
/* Now we have found X ....
* X = string at Node tmp
* Check : if s[idx] X s[idx] already exists or not*/
if (tree[tmp].insertEdg[s[idx] - 'a'] != 0) {
// s[idx] X s[idx] already exists in the tree
currNode = tree[tmp].insertEdg[s[idx] - 'a'];
return;
}
// creating new Node
ptr++;
// making new Node as child of X with
// weight as s[idx]
tree[tmp].insertEdg[s[idx] - 'a'] = ptr;
// calculating length of new Node
tree[ptr].length = tree[tmp].length + 2;
// updating end point for new Node
tree[ptr].end = idx;
// updating the start for new Node
tree[ptr].start = idx - tree[ptr].length + 1;
// STEP 2//
/* Setting the suffix edge for the newly created
Node tree[ptr]. Finding some String Y such that
s[idx] + Y + s[idx] is longest possible
palindromic suffix for newly created Node.*/
tmp = tree[tmp].suffixEdg;
// making new Node as current Node
currNode = ptr;
if (tree[currNode].length == 1) {
// if new palindrome's length is 1
// making its suffix link to be null string
tree[currNode].suffixEdg = 2;
return;
}
while (true) {
int curLength = tree[tmp].length;
if (idx - curLength >= 1
and s[idx] == s[idx - curLength - 1])
break;
tmp = tree[tmp].suffixEdg;
}
// Now we have found string Y
// linking current Nodes suffix link with
// s[idx]+Y+s[idx]
tree[currNode].suffixEdg
= tree[tmp].insertEdg[s[idx] - 'a'];
}
// driver program
int main()
{
// initializing the tree
root1.length = -1;
root1.suffixEdg = 1;
root2.length = 0;
root2.suffixEdg = 1;
tree[1] = root1;
tree[2] = root2;
ptr = 2;
currNode = 1;
// given string
s = "forgeeksskeegfor";
int l = s.length();
for (int i = 0; i < l; i++)
insert(i);
int maxx = 0;
string result = "";
for (int i = 3; i <= ptr; i++) {
string res = "";
for (int j = tree[i].start; j <= tree[i].end; j++)
res += s[j];
if (res.size() > maxx) {
maxx = res.size();
result = res;
}
}
cout << result;
return 0;
}
Java
// Nikunj Sonigara
// Define a class named Main
class Main {
// Define a static nested class named Node
static class Node {
int start, end; // Represents the start and end indices of a palindrome substring
int length; // Length of the palindrome substring
int[] insertEdge = new int[26]; // Array to store indices of child nodes for each character
int suffixEdge; // Index of the node representing the largest proper suffix palindrome
}
// Create two root nodes and an array to store nodes
static Node root1 = new Node();
static Node root2 = new Node();
static Node[] tree = new Node[1000];
static int ptr; // Pointer to the current node being processed
static int currNode; // Index of the current node in the tree
static String s; // Input string
// Main method
public static void main(String[] args) {
// Initialize the root nodes
root1.length = -1;
root1.suffixEdge = 1;
root2.length = 0;
root2.suffixEdge = 1;
// Create nodes and initialize the tree array
for (int i = 0; i < 1000; i++) {
tree[i] = new Node();
}
tree[1] = root1;
tree[2] = root2;
ptr = 2;
currNode = 1;
// Input string
s = "forgeeksskeegfor";
int l = s.length();
// Build the palindrome tree
for (int i = 0; i < l; i++) {
insert(i);
}
// Find the longest palindrome substring in the tree
int maxx = 0;
String result = "";
for (int i = 3; i <= ptr; i++) {
StringBuilder res = new StringBuilder();
for (int j = tree[i].start; j <= tree[i].end; j++) {
res.append(s.charAt(j));
}
if (res.length() > maxx) {
maxx = res.length();
result = res.toString();
}
}
// Print the result
System.out.println(result);
}
// Method to insert a character into the palindrome tree
static void insert(int idx) {
int tmp = currNode;
// Traverse the tree to find the largest proper suffix palindrome
while (true) {
int curLength = tree[tmp].length;
if (idx - curLength >= 1 && s.charAt(idx) == s.charAt(idx - curLength - 1)) {
break;
}
tmp = tree[tmp].suffixEdge;
}
// Check if the current character already has an edge in the tree
if (tree[tmp].insertEdge[s.charAt(idx) - 'a'] != 0) {
currNode = tree[tmp].insertEdge[s.charAt(idx) - 'a'];
return;
}
// Create a new node for the current character
ptr++;
tree[tmp].insertEdge[s.charAt(idx) - 'a'] = ptr;
tree[ptr].length = tree[tmp].length + 2;
tree[ptr].end = idx;
tree[ptr].start = idx - tree[ptr].length + 1;
// Update the current node and handle special case for length 1 palindrome
tmp = tree[tmp].suffixEdge;
currNode = ptr;
if (tree[currNode].length == 1) {
tree[currNode].suffixEdge = 2;
return;
}
// Traverse the tree again to find the largest proper suffix palindrome
while (true) {
int curLength = tree[tmp].length;
if (idx - curLength >= 1 && s.charAt(idx) == s.charAt(idx - curLength - 1)) {
break;
}
tmp = tree[tmp].suffixEdge;
}
// Update the suffix edge for the current node
tree[currNode].suffixEdge = tree[tmp].insertEdge[s.charAt(idx) - 'a'];
}
}
Python3
# Python program to demonstrate working of
# palindromic tree
MAXN = 1000
class Node:
def __init__(self):
# store start and end indexes of current Node inclusively
self.start = 0
self.end = 0
# stores length of substring
self.length = 0
# stores insertion Node for all characters a-z
self.insertEdg = [0]*26
# stores the Maximum Palindromic Suffix Node for the current Node
self.suffixEdg = 0
# two special dummy Nodes as explained above
root1 = Node()
root2 = Node()
# stores Node information for constant time access
tree = [Node() for i in range(MAXN)]
# Keeps track the current Node while insertion
currNode = 0
s = ""
ptr = 0
def insert(idx):
global currNode, ptr
# STEP 1
# search for Node X such that s[idx] X S[idx] is maximum palindrome ending at position idx
# iterate down the suffix link of currNode to find X
tmp = currNode
while True:
curLength = tree[tmp].length
if idx - curLength >= 1 and s[idx] == s[idx - curLength - 1]:
break
tmp = tree[tmp].suffixEdg
# Now we have found X....
# X = string at Node tmp
# Check: if s[idx] X s[idx] already exists or not
if tree[tmp].insertEdg[ord(s[idx]) - ord('a')] != 0:
# s[idx] X s[idx] already exists in the tree
currNode = tree[tmp].insertEdg[ord(s[idx]) - ord('a')]
return
# creating new Node
ptr += 1
# making new Node as child of X with weight as s[idx]
tree[tmp].insertEdg[ord(s[idx]) - ord('a')] = ptr
# calculating length of new Node
tree[ptr].length = tree[tmp].length + 2
# updating end point for new Node
tree[ptr].end = idx
# updating the start for new Node
tree[ptr].start = idx - tree[ptr].length + 1
# STEP 2
# Setting the suffix edge for the newly created Node tree[ptr]. Finding some String Y
# such that s[idx] + Y + s[idx] is longest possible palindromic suffix for newly created Node.
tmp = tree[tmp].suffixEdg
# making new Node as current Node
currNode = ptr
if tree[currNode].length == 1:
# if new palindrome's length is 1, making its suffix link to be null string
tree[currNode].suffixEdg = 2
return
while True:
curLength = tree[tmp].length
if idx - curLength >= 1 and s[idx] == s[idx - curLength - 1]:
break
tmp = tree[tmp].suffixEdg
# Now we have found string Y
# linking current Nodes suffix link with s[idx]+Y+s[idx]
tree[currNode].suffixEdg = tree[tmp].insertEdg[ord(s[idx]) - ord('a')]
# driver program
# initializing the tree
root1.length = -1
root1.suffixEdg = 1
root2.length = 0
root2.suffixEdg = 1
tree[1] = root1
tree[2] = root2
ptr = 2
currNode = 1
# given string
s = "forgeeksskeegfor"
l = len(s)
for i in range(l):
insert(i)
maxx = 0
result = ""
for i in range(3, ptr+1):
res = ""
for j in range(tree[i].start, tree[i].end+1):
res += s[j]
if len(res) > maxx:
maxx = len(res)
result = res
print(result)
C#
using System;
class PalindromicTree {
const int MAXN = 1000;
// Node structure
struct Node
{
// Store start and end indexes of current Node
// inclusively
public int start, end;
// Store length of substring
public int length;
// Store insertion Node for all characters a-z
public int[] insertEdge;
// Store the Maximum Palindromic Suffix Node for the
// current Node
public int suffixEdge;
}
// Two special dummy Nodes as explained above
static Node root1, root2;
// Store Node information for constant time access
static Node[] tree;
// Keep track of the current Node while insertion
static int currNode;
static string s;
static int ptr;
// Function to insert a character into the Palindromic
// Tree
static void Insert(int idx)
{
// STEP 1
/* Search for Node X such that s[idx] X s[idx]
* is the maximum palindrome ending at position idx.
* Iterate down the suffix link of currNode to find
* X. */
int tmp = currNode;
while (true) {
int curLength = tree[tmp].length;
if (idx - curLength >= 1
&& s[idx] == s[idx - curLength - 1])
break;
tmp = tree[tmp].suffixEdge;
}
/* Now we have found X...
* X = string at Node tmp
* Check: if s[idx] X s[idx] already exists or not
*/
if (tree[tmp].insertEdge[s[idx] - 'a'] != 0) {
// s[idx] X s[idx] already exists in the tree
currNode = tree[tmp].insertEdge[s[idx] - 'a'];
return;
}
// Creating a new Node
ptr++;
// Initializing the insertEdge array for the new
// Node
tree[ptr].insertEdge = new int[26];
// Making the new Node as a child of X with weight
// as s[idx]
tree[tmp].insertEdge[s[idx] - 'a'] = ptr;
// Calculating the length of the new Node
tree[ptr].length = tree[tmp].length + 2;
// Updating the end point for the new Node
tree[ptr].end = idx;
// Updating the start for the new Node
tree[ptr].start = idx - tree[ptr].length + 1;
// STEP 2
/* Setting the suffix edge for the newly created
* Node tree[ptr]. Finding some String Y such that
* s[idx] + Y + s[idx] is the longest possible
* palindromic suffix for the newly created Node. */
tmp = tree[tmp].suffixEdge;
// Making the new Node as the current Node
currNode = ptr;
if (tree[currNode].length == 1) {
// If the new palindrome's length is 1,
// making its suffix link to be a null string
tree[currNode].suffixEdge = 2;
return;
}
while (true) {
int curLength = tree[tmp].length;
if (idx - curLength >= 1
&& s[idx] == s[idx - curLength - 1])
break;
tmp = tree[tmp].suffixEdge;
}
// Now we have found string Y
// Linking the current Node's suffix link with
// s[idx]+Y+s[idx]
tree[currNode].suffixEdge
= tree[tmp].insertEdge[s[idx] - 'a'];
}
// Driver program
static void Main()
{
// Initializing the tree
root1.length = -1;
root1.suffixEdge = 1;
root1.insertEdge = new int[26];
root2.length = 0;
root2.suffixEdge = 1;
root2.insertEdge = new int[26];
tree = new Node[MAXN];
tree[1] = root1;
tree[2] = root2;
ptr = 2;
currNode = 1;
// Given string
s = "forgeeksskeegfor";
int l = s.Length;
for (int i = 0; i < l; i++)
Insert(i);
int maxx = 0;
string result = "";
for (int i = 3; i <= ptr; i++) {
string res = "";
for (int j = tree[i].start; j <= tree[i].end;
j++)
res += s[j];
if (res.Length > maxx) {
maxx = res.Length;
result = res;
}
}
Console.WriteLine(result);
}
}
JavaScript
// JavaScript program to demonstrate working of
// palindromic tree
const MAXN = 1000;
class Node {
constructor() {
this.start = 0; // store start and end indexes of current Node inclusively
this.end = 0;
this.length = 0; // stores length of substring
this.insertEdg = Array(26).fill(0); // stores insertion Node for all characters a-z
this.suffixEdg = 0; // stores the Maximum Palindromic Suffix Node for the current Node
}
}
const root1 = new Node(); // two special dummy Nodes as explained above
const root2 = new Node();
const tree = Array(MAXN).fill().map(() => new Node()); // stores Node information for constant time access
let currNode = 0;
let s = "";
let ptr = 0;
function insert(idx) {
let tmp;
// STEP 1
// search for Node X such that s[idx] X S[idx] is maximum palindrome ending at position idx
// iterate down the suffix link of currNode to find X
tmp = currNode;
while (true) {
const curLength = tree[tmp].length;
if (idx - curLength >= 1 && s[idx] === s[idx - curLength - 1]) {
break;
}
tmp = tree[tmp].suffixEdg;
}
// Now we have found X....
// X = string at Node tmp
// Check: if s[idx] X s[idx] already exists or not
if (tree[tmp].insertEdg[s.charCodeAt(idx) - 97] !== 0) {
// s[idx] X s[idx] already exists in the tree
currNode = tree[tmp].insertEdg[s.charCodeAt(idx) - 97];
return;
}
// creating new Node
ptr += 1;
// making new Node as child of X with weight as s[idx]
tree[tmp].insertEdg[s.charCodeAt(idx) - 97] = ptr;
// calculating length of new Node
tree[ptr].length = tree[tmp].length + 2;
// updating end point for new Node
tree[ptr].end = idx;
// updating the start for new Node
tree[ptr].start = idx - tree[ptr].length + 1;
// STEP 2
// Setting the suffix edge for the newly created Node tree[ptr]. Finding some String Y
// such that s[idx] + Y + s[idx] is longest possible palindromic suffix for newly created Node.
tmp = tree[tmp].suffixEdg;
// making new Node as current Node
currNode = ptr;
if (tree[currNode].length === 1) {
// if new palindrome's length is 1, making its suffix link to be null string
tree[currNode].suffixEdg = 2;
return;
}
while (true) {
const curLength = tree[tmp].length;
if (idx - curLength >= 1 && s[idx] === s[idx - curLength - 1]) {
break;
}
tmp = tree[tmp].suffixEdg;
}
// Now we have found string Y
// linking current Nodes suffix link with s[idx]+Y+s[idx]
tree[currNode].suffixEdg = tree[tmp].insertEdg[s.charCodeAt(idx) - 97];
}
// initializing the tree
// const root1 = new Node();
root1.length = -1;
root1.suffixEdg = 1;
// const root2 = new Node();
root2.length = 0;
root2.suffixEdg = 1;
tree[1] = root1
tree[2] = root2
ptr = 2;
currNode = 1;
s = "forgeeksskeegfor";
const l = s.length;
for (let i = 0; i < l; i++) {
insert(i);
}
let maxx = 0;
let result = "";
for (let i = 3; i <= ptr; i++) {
let res = "";
for (let j = tree[i].start; j <= tree[i].end; j++) {
res += s[j];
}
if (res.length > maxx) {
maxx = res.length;
result = res;
}
}
console.log(result);
// Contributed by adityasha4x71
Time Complexity:
The time complexity for the building process will be O(k*n), here “n” is the length of the string and ‘k‘ is the extra iterations required to find the string X and string Y in the suffix links every time we insert a character.
Let’s try to approximate the constant ‘k’. We shall consider a worst case like s = “aaaaaabcccccccdeeeeeeeeef”. In this case for similar streak of continuous characters it will take extra 2 iterations per index to find both string X and Y in the suffix links , but as soon as it reaches some index i such that s[i]!=s[i-1] the left most pointer for the maximum length suffix will reach its rightmost limit. Therefore, for all i when s[i]!=s[i-1] , it will cost in total n iterations(summing over each iteration) and for rest i when s[i]==s[i-1] it takes 2 iteration which sums up over all such i and takes 2*n iterations. Hence, approximately our complexity in this case will be O(3*n) ~ O(n). So, we can roughly say that the constant factor ‘k’ will be very less. Therefore, we can consider the overall complexity to be linear O(length of string). You may refer the reference links for better understanding.
Similar Reads
Basics & Prerequisites
Data Structures
Array Data StructureIn this article, we introduce array, implementation in different popular languages, its basic operations and commonly seen problems / interview questions. An array stores items (in case of C/C++ and Java Primitive Arrays) or their references (in case of Python, JS, Java Non-Primitive) at contiguous
3 min read
String in Data StructureA string is a sequence of characters. The following facts make string an interesting data structure.Small set of elements. Unlike normal array, strings typically have smaller set of items. For example, lowercase English alphabet has only 26 characters. ASCII has only 256 characters.Strings are immut
2 min read
Hashing in Data StructureHashing is a technique used in data structures that efficiently stores and retrieves data in a way that allows for quick access. Hashing involves mapping data to a specific index in a hash table (an array of items) using a hash function. It enables fast retrieval of information based on its key. The
2 min read
Linked List Data StructureA linked list is a fundamental data structure in computer science. It mainly allows efficient insertion and deletion operations compared to arrays. Like arrays, it is also used to implement other data structures like stack, queue and deque. Hereâs the comparison of Linked List vs Arrays Linked List:
2 min read
Stack Data StructureA Stack is a linear data structure that follows a particular order in which the operations are performed. The order may be LIFO(Last In First Out) or FILO(First In Last Out). LIFO implies that the element that is inserted last, comes out first and FILO implies that the element that is inserted first
2 min read
Queue Data StructureA Queue Data Structure is a fundamental concept in computer science used for storing and managing data in a specific order. It follows the principle of "First in, First out" (FIFO), where the first element added to the queue is the first one to be removed. It is used as a buffer in computer systems
2 min read
Tree Data StructureTree Data Structure is a non-linear data structure in which a collection of elements known as nodes are connected to each other via edges such that there exists exactly one path between any two nodes. Types of TreeBinary Tree : Every node has at most two childrenTernary Tree : Every node has at most
4 min read
Graph Data StructureGraph Data Structure is a collection of nodes connected by edges. It's used to represent relationships between different entities. If you are looking for topic-wise list of problems on different topics like DFS, BFS, Topological Sort, Shortest Path, etc., please refer to Graph Algorithms. Basics of
3 min read
Trie Data StructureThe Trie data structure is a tree-like structure used for storing a dynamic set of strings. It allows for efficient retrieval and storage of keys, making it highly effective in handling large datasets. Trie supports operations such as insertion, search, deletion of keys, and prefix searches. In this
15+ min read
Algorithms
Searching AlgorithmsSearching algorithms are essential tools in computer science used to locate specific items within a collection of data. In this tutorial, we are mainly going to focus upon searching in an array. When we search an item in an array, there are two most common algorithms used based on the type of input
2 min read
Sorting AlgorithmsA Sorting Algorithm is used to rearrange a given array or list of elements in an order. For example, a given array [10, 20, 5, 2] becomes [2, 5, 10, 20] after sorting in increasing order and becomes [20, 10, 5, 2] after sorting in decreasing order. There exist different sorting algorithms for differ
3 min read
Introduction to RecursionThe process in which a function calls itself directly or indirectly is called recursion and the corresponding function is called a recursive function. A recursive algorithm takes one step toward solution and then recursively call itself to further move. The algorithm stops once we reach the solution
14 min read
Greedy AlgorithmsGreedy algorithms are a class of algorithms that make locally optimal choices at each step with the hope of finding a global optimum solution. At every step of the algorithm, we make a choice that looks the best at the moment. To make the choice, we sometimes sort the array so that we can always get
3 min read
Graph AlgorithmsGraph is a non-linear data structure like tree data structure. The limitation of tree is, it can only represent hierarchical data. For situations where nodes or vertices are randomly connected with each other other, we use Graph. Example situations where we use graph data structure are, a social net
3 min read
Dynamic Programming or DPDynamic Programming is an algorithmic technique with the following properties.It is mainly an optimization over plain recursion. Wherever we see a recursive solution that has repeated calls for the same inputs, we can optimize it using Dynamic Programming. The idea is to simply store the results of
3 min read
Bitwise AlgorithmsBitwise algorithms in Data Structures and Algorithms (DSA) involve manipulating individual bits of binary representations of numbers to perform operations efficiently. These algorithms utilize bitwise operators like AND, OR, XOR, NOT, Left Shift, and Right Shift.BasicsIntroduction to Bitwise Algorit
4 min read
Advanced
Segment TreeSegment Tree is a data structure that allows efficient querying and updating of intervals or segments of an array. It is particularly useful for problems involving range queries, such as finding the sum, minimum, maximum, or any other operation over a specific range of elements in an array. The tree
3 min read
Pattern SearchingPattern searching algorithms are essential tools in computer science and data processing. These algorithms are designed to efficiently find a particular pattern within a larger set of data. Patten SearchingImportant Pattern Searching Algorithms:Naive String Matching : A Simple Algorithm that works i
2 min read
GeometryGeometry is a branch of mathematics that studies the properties, measurements, and relationships of points, lines, angles, surfaces, and solids. From basic lines and angles to complex structures, it helps us understand the world around us.Geometry for Students and BeginnersThis section covers key br
2 min read
Interview Preparation
Practice Problem