 Open in App
Not now

# Longest Palindromic Substring using Palindromic Tree | Set 3

• Difficulty Level : Expert
• Last Updated : 16 Mar, 2023

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:

1. Insertion edge (weighted edge)
2. 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;`   `    ``// 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 = root1;` `    ``tree = 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;` `}`

## 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)`

## 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 = root1` `tree = 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`

Output

`geeksskeeg`

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.

My Personal Notes arrow_drop_up
Related Articles