Approach: Use an Eertree (palindromic tree) with two root nodes (length -1 and 0). Process characters left-to-right, find the largest suffix-palindrome that can be extended, create new node when a new palindrome appears, and track occurrence counts. After building, propagate occurrence counts from longer palindromes to their suffix links to get total occurrences.cpp
#include <bits/stdc++.h>
using namespace std;
struct Node {
int len; // palindrome length
int link; // suffix link (largest proper palindromic suffix)
array<int,26> next{}; // transitions by char
long long occ = 0; // occurrences as end positions
int firstPos = -1; // optional: end position of first occurrence
Node(int l=0): len(l), link(0) { next.fill(0); }
};
struct Eertree {
string s;
vector<Node> tree;
int last; // node representing largest suffix-palindrome of processed prefix
Eertree(int n=0) {
tree.reserve(n+5);
tree.push_back(Node(-1)); // 0: root with len -1
tree.push_back(Node(0)); // 1: root with len 0
tree[0].link = 0;
tree[1].link = 0;
last = 1;
}
void build(const string &str) {
s = str;
for (int i = 0; i < (int)s.size(); ++i) addChar(i);
// propagate occurrences: iterate nodes by decreasing length
vector<int> idx(tree.size());
iota(idx.begin(), idx.end(), 0);
sort(idx.begin(), idx.end(), [&](int a,int b){ return tree[a].len > tree[b].len; });
for (int id : idx) if (id>1) tree[tree[id].link].occ += tree[id].occ;
}
void addChar(int pos) {
int cur = last;
int c = s[pos]-'a';
while (true) {
int curlen = tree[cur].len;
if (pos-1-curlen >= 0 && s[pos-1-curlen]==s[pos]) break;
cur = tree[cur].link;
}
if (tree[cur].next[c]) {
last = tree[cur].next[c];
tree[last].occ++;
return;
}
// create new node
Node node(tree[cur].len + 2);
tree.push_back(node);
int newNode = tree.size()-1;
tree[cur].next[c] = newNode;
tree[newNode].occ = 1;
tree[newNode].firstPos = pos;
if (tree[newNode].len == 1) {
tree[newNode].link = 1;
last = newNode;
return;
}
// find suffix link for new node
int linkCandidate = tree[cur].link;
while (true) {
int candidLen = tree[linkCandidate].len;
if (pos-1-candidLen >=0 && s[pos-1-candidLen]==s[pos]) break;
linkCandidate = tree[linkCandidate].link;
}
tree[newNode].link = tree[linkCandidate].next[c];
last = newNode;
}
// Return vector of pairs (palindrome_string, count)
vector<pair<string,long long>> getPalindromes() {
vector<pair<string,long long>> res;
for (int i = 2; i < (int)tree.size(); ++i) {
int len = tree[i].len;
int endPos = tree[i].firstPos;
string p = s.substr(endPos - len + 1, len);
res.emplace_back(p, tree[i].occ);
}
return res;
}
};
Key points:- Build is O(n) since each character walks suffix links amortized constant and creates ≤ n nodes.- Space O(n) nodes; each node has 26 transitions (or use map for sparse alphabets).- Edge cases: empty string (returns empty list), non-lowercase input (adapt alphabet), large alphabet (use unordered_map<char,int> per node).- Alternative: Manacher finds palindromic radii but doesn't enumerate distinct palindromes with counts as directly.