Definition
A palindromic tree (EER tree, also called a palindromic automaton) is an efficient data structure that stores all palindromic substrings of a string. It was first published by Mikhail Rubinchik and Arseny M. Shur in 2015. Inspired by suffix trees and other suffix-oriented string structures, it gives simple and efficient solutions to many palindrome problems.
Structure
A palindromic tree looks roughly like this.

As with other automata, a palindromic tree consists of transition edges and suffix links (fail pointers), and each node represents a palindromic substring.
Because palindromes can have odd or even length, one could insert a character outside the alphabet, such as '#', as a separator, as in Manacher's algorithm, so that all palindrome lengths become odd. This is inconvenient, however. Is there a better method?
Yes. Build two trees: nodes in one tree represent palindromes of odd length, and nodes in the other represent palindromes of even length.
As in other automata, a node's fail pointer points to the node representing the longest palindromic suffix of its palindrome. A transition edge does not append one character to the original palindrome; it adds the same character to both ends, which is necessary to preserve palindromicity.
Each node also stores len, the length of its palindrome. This information makes the tree easy to construct.
Construction
A palindromic tree has two initial states representing palindromes of lengths , called the odd root and even root. They do not represent actual strings; they only serve as initial states, just like roots of other automata.
The even root's fail pointer points to the odd root. The odd root's fail pointer is irrelevant: the odd root cannot fail, because every transition from it has length , a single-character palindrome.
As with a suffix automaton, construct the palindromic tree incrementally.
Suppose the tree for the first characters has been built, and we append the character at position of the original string.
Starting from the node representing the longest palindromic suffix ending at the previous character, repeatedly follow fail pointers until finding a node satisfying ; that is, the character immediately before the represented palindrome equals the character being appended.
Here is the corresponding figure from the paper.

Following fail pointers finds the node for A; adding X to both ends gives the current palindrome XAX. This node is the tree node for the longest palindromic suffix ending at . The length- node is useful here: if no X matches, the condition reduces to , so the node representing the one-character palindrome X is obtained naturally. The pair X and XAX is therefore handled by the same transition. If this node does not yet exist, create it.
We must then find the fail pointer of the new node. Again follow fail pointers from A to find the longest palindromic suffix XBX of XAX, and set the fail pointer to its node.
This node never needs to be created: the first characters and last characters of A are both B; the two ends of the first part correspond to X by palindromicity, and the second part is fixed as X, so the same -length construction gives XBX, which must already be present.
If no fail transition matches, connect the node to the length- node, which is valid because the empty string is a suffix of every node.
Proof of the linear number of states
Theorem
For a string , the number of distinct palindromic substrings is at most .
Proof
Use mathematical induction.
-
If , the string has one character and one substring, which is a palindrome, so the claim holds.
-
If , let , where is obtained by appending to , and assume the claim holds for . Consider palindromic substrings ending at the final character , and sort their left endpoints as . Since is a palindrome, for every we have . Hence, for , the palindrome already occurred in . Each appended character therefore introduces at most one new distinct palindrome, and the first such position is .
By induction, the theorem follows.
Thus the palindromic tree has states. Each state represents exactly one distinct palindrome, and the state that transitions to a node is unique, so the total number of transitions is also .
Correctness proof
In the figure above, when the current character X is added, the linear-state proof says that we only need to find the longest palindromic suffix containing the final character, namely XAX. We then find its longest palindromic suffix XBX and create the suffix link. The state for XBX already exists; its palindromic suffix containing the final character is XAX, and XBX itself and all ancestors of its state in the fail tree are therefore already represented.
For the palindromic tree of , let . Apart from following fail pointers, all operations are clearly .
When a character is added, each fail jump changes the fail-tree depth by , while adding a fail link changes it by in the accounting below. When fail is , reaching , the net increase is from the negative-one node.
Only characters are added, so the depth can increase at most times and there can be at most fail jumps.
Therefore, constructing the palindromic tree of takes time.
Applications
Number of distinct palindromic substrings
By the linear-state theorem, the number of distinct palindromic substrings of a string equals the number of palindromic-tree states, excluding the odd and even roots.
Number of occurrences of palindromic substrings
Build the palindromic tree and count occurrences similarly to a suffix automaton.
Nodes are inserted in topological order during construction. Enumerate all states in reverse order and add each state's occurrence count to the count of the state pointed to by its fail link.
Example: 「APIO2014」Palindrome
Define the existence value of a substring of as its number of occurrences in multiplied by its length. Given , find the maximum existence value among its palindromic substrings.
#include <algorithm>
#include <cstring>
#include <iostream>
#include <string>
using namespace std;
constexpr int MAXN = 300000 + 5;
namespace pam {
int sz, tot, last;
int cnt[MAXN], ch[MAXN][26], len[MAXN], fail[MAXN];
char s[MAXN];
int node(int l) { // Create a new node of length l
sz++;
memset(ch[sz], 0, sizeof(ch[sz]));
len[sz] = l;
fail[sz] = cnt[sz] = 0;
return sz;
}
void clear() { // Initialize
sz = -1;
last = 0;
s[tot = 0] = '$';
node(0);
node(-1);
fail[0] = 1;
}
int getfail(int x) { // Find a palindromic suffix
while (s[tot - len[x] - 1] != s[tot]) x = fail[x];
return x;
}
void insert(char c) { // Build the tree
s[++tot] = c;
int now = getfail(last);
if (!ch[now][c - 'a']) {
int x = node(len[now] + 2);
fail[x] = ch[getfail(fail[now])][c - 'a'];
ch[now][c - 'a'] = x;
}
last = ch[now][c - 'a'];
cnt[last]++;
}
long long solve() {
long long ans = 0;
for (int i = sz; i >= 0; i--) {
cnt[fail[i]] += cnt[i];
}
for (int i = 1; i <= sz; i++) { // Update the answer
ans = max(ans, 1ll * len[i] * cnt[i]);
}
return ans;
}
} // namespace pam
string s;
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
pam::clear();
cin >> s;
for (int i = 0; i < s.size(); i++) {
pam::insert(s[i]);
}
cout << pam::solve() << '\n';
return 0;
}
Minimum palindromic partition
Given a string , find the minimum such that there are strings , each a palindrome, whose concatenation in order equals .
Consider DP, where is the minimum number of parts for the length- prefix of . The transition enumerates all palindromes ending at character :
There can be palindromic substrings, so this takes time and is too slow. The following lemmas optimize the transition.
Let the prefix of of length be and the suffix of length be .
Period: if and , then is a period of .
Border: if and , then is a border of .
Relation between periods and borders: is a border of if and only if is a period of .
Proof
If is a border of , then , so . Hence is a period of .
If is a period of , then , so and is a border of .
Lemma 1
For a palindromic string and a suffix of the string, the suffix is a border of if and only if is a palindrome.
Proof
For , since and are palindromes, , so is a border of .
Conversely, for , if is a border of , . Since is a palindrome, the same string gives , hence and is a palindrome.
The positions with the same color in the figure represent equal characters.

Lemma 2
Let be a border of with . Then is a palindrome if and only if is a palindrome.
Proof
If is a palindrome, Lemma shows that the suffix and this are also palindromes.
If is a palindrome, this has the border relation . Since , is also a palindrome.
Lemma 3
If is a border of a palindromic string, then is a period of . It is the minimum period of if and only if is the longest proper palindromic suffix of ; both quantities refer to .
Lemma 4
Let be a palindrome, and let be the longest proper palindromic suffix of ; let be the longest proper palindromic suffix of . Let satisfy . Then:
-
;
-
If , then ;
-
If , then .

Proof
- By the consequence of Lemma , is the minimum period of , and is the minimum period of . If , then, because is a suffix of , is a period of both and , contradicting the minimality of for . Thus .
- Since is a border of , is a prefix of . Write , so is a border of . If , then ; Lemma makes a palindrome, and Lemma makes a border of . Since , this gives , a contradiction. Therefore , with the same notation.
- Both are prefixes of and have , so .

Corollary
When all palindromic suffixes of are sorted by length, they can be divided into arithmetic-progression segments.
Proof
Sort the lengths of the palindromic suffixes of increasingly as . For , if , then form an arithmetic progression. Otherwise, , and Lemma gives and , so . Whenever the difference changes, the largest length therefore more than doubles relative to the smallest. This can happen only times, so the lengths of the palindromic suffixes of form arithmetic-progression segments.
The corollary can also be proved with the weak periodicity lemma by grouping the borders of the longest palindromic suffix according to their length , where . Considering the longest border in each group gives groups. Detailed proofs can be found in Jin Ce's Selected String Algorithms and Chen Sunli's 2019 IOI National Candidate Team paper Algorithms and Applications for Substring Period Queries.
With this result, we can optimize the transition of .
Optimization
For every node in the palindromic tree, maintain and . Here is the difference between the lengths represented by and , namely . The pointer follows fail pointers from to the first node with ; it is the shortest-length node in the arithmetic-progression segment containing .
By the result above, following requires only jumps for each appended character. We can therefore store the sum of the values of all palindromes in one arithmetic-progression segment (the original problem uses ) at the node representing its longest palindrome.
Let be the sum of the values in the arithmetic-progression segment containing , where is its longest node. Then , with the current index.
To update and , suppose the current character is at index and its palindromic-tree node is . The orange positions in the figure contribute to ; the shortest palindrome belongs to the next segment. The previous occurrence of ends at , and contains the blue values. Thus is plus one additional value at , where the previous endpoint is also . Use to update , then repeat while following ; the new contribution is included in each step.

The correctness relies on the following fact: if and belong to the same arithmetic-progression segment, the previous occurrence of is at .
Proof
- By Lemma , is a border of , so it occurs at .
Suppose occurs at position in . Since and are in the same arithmetic progression, . The extra occurrence of overlaps the occurrence at ; call the overlap , and let satisfy . As in Lemma , is a palindrome, and the prefix of is also a palindrome. This contradicts the fact that is the longest palindromic prefix (and suffix) of , whose previous occurrence is again .
Example: Codeforces 932G Palindrome Partition
Given a string , partition it into , where is even and . Count the valid partitions of .
Solution
Construct . The problem becomes counting even-palindrome partitions of : change the transition above from a minimum to a sum and update only at even positions. The time complexity is and the space complexity is .
#include <cstring>
#include <iostream>
#include <string>
using namespace std;
using ll = long long;
constexpr int mod = 1e9 + 7;
constexpr int MAXN = 1000000 + 5;
int add(int x, int y) {
x += y;
return x >= mod ? x -= mod : x;
}
namespace pam {
int sz, tot, last;
int ch[MAXN][26], len[MAXN], fail[MAXN];
int cnt[MAXN], dep[MAXN], dif[MAXN], slink[MAXN];
char s[MAXN];
int node(int l) { // Create a new node of length l
sz++;
memset(ch[sz], 0, sizeof(ch[sz]));
len[sz] = l;
fail[sz] = 0;
cnt[sz] = 0;
dep[sz] = 0;
return sz;
}
void clear() { // Initialize
sz = -1;
last = 0;
s[tot = 0] = '$';
node(0);
node(-1);
fail[0] = 1;
}
int getfail(int x) { // Find a palindromic suffix
while (s[tot - len[x] - 1] != s[tot]) x = fail[x];
return x;
}
void insert(char c) { // Build the tree
s[++tot] = c;
int now = getfail(last);
if (!ch[now][c - 'a']) {
int x = node(len[now] + 2);
fail[x] = ch[getfail(fail[now])][c - 'a'];
dep[x] = dep[fail[x]] + 1;
ch[now][c - 'a'] = x;
dif[x] = len[x] - len[fail[x]];
if (dif[x] == dif[fail[x]])
slink[x] = slink[fail[x]];
else
slink[x] = fail[x];
}
last = ch[now][c - 'a'];
cnt[last]++;
}
} // namespace pam
using pam::dif;
using pam::fail;
using pam::len;
using pam::slink;
int n, dp[MAXN], g[MAXN];
string s;
char t[MAXN];
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
pam::clear();
cin >> s;
n = s.size();
s = " " + s;
for (int i = 1, j = 0; i <= n; i++) t[++j] = s[i], t[++j] = s[n - i + 1];
dp[0] = 1;
for (int i = 1; i <= n; i++) {
pam::insert(t[i]);
for (int x = pam::last; x > 1; x = slink[x]) {
g[x] = dp[i - len[slink[x]] - dif[x]];
if (dif[x] == dif[fail[x]]) g[x] = add(g[x], g[fail[x]]);
if (i % 2 == 0) dp[i] = add(dp[i], g[x]); // Update the dp array at even positions
}
}
cout << dp[n];
return 0;
}
Example problems
Further reading
-
EERTREE: An Efficient Data Structure for Processing Palindromes in Strings
-
2017 IOI National Candidate Team papers, Palindromic Trees and Their Applications, Weng Wentao
-
2019 IOI National Candidate Team papers, Algorithms and Applications for Substring Period Queries, Chen Sunli
-
Selected String Algorithms, Jin Ce
-
A Subquadratic Algorithm for Minimum Palindromic Factorization