#include <bits/stdc++.h>
using namespace std;
// ===================================================================
// This file contains a complete collection of Palindromic Tree (Eertree)
// algorithms. Each function is ready to be used as a "black box".
//
// READ THIS FIRST:
// A Palindromic Tree (also called Eertree) is a data structure that stores
// all distinct palindromic substrings of a string in O(n) time and memory.
//
// Key concepts you need to understand:
// - "Palindrome": a string that reads the same forwards and backwards.
// Example: "racecar", "abba", "a", "aa".
// - "Palindromic Tree": a tree-like structure where each node represents
// a distinct palindrome. The tree has two roots:
// * Root 0: represents the empty string (length -1) - odd length root.
// * Root 1: represents the imaginary string (length 0) - even length root.
// - "Suffix link" (or "link"): a pointer from a node to the longest proper
// palindromic suffix of that node's palindrome.
// - "len[node]": the length of the palindrome represented by this node.
// - "occ[node]": how many times this palindrome appears in the string.
// - "num[node]": how many distinct palindromic suffixes of the palindrome
// represented by this node.
// - "diff[node]" and "series link": advanced concepts for efficient DP
// (explained later).
//
// All functions in this file assume the Palindromic Tree has been built
// by calling addChar() for each character of the input string.
// ===================================================================
// ===================================================================
// Class: PalindromicTree
// This class represents a Palindromic Tree (Eertree) for a given string.
// ===================================================================
class PalindromicTree {
public:
// -----------------------------------------------------------------
// Data members (exposed so you can read results after building)
// -----------------------------------------------------------------
// next[node][c] = node id of the palindrome formed by adding character c
// on both sides of the palindrome represented by 'node'.
// -1 means no such palindrome exists.
vector<array<int, 26>> next; // Works for lowercase English letters 'a'..'z'
// len[node] = length of the palindrome represented by this node.
vector<int> len;
// link[node] = node id of the longest proper palindromic suffix.
// For root 0 (len=-1), link[0] = 0 (points to itself).
// For root 1 (len=0), link[1] = 0.
vector<int> link;
// occ[node] = total number of occurrences of this palindrome
// in the processed string (after counting with countOccurrences()).
vector<long long> occ;
// num[node] = number of distinct palindromic suffixes of the palindrome
// represented by this node (including itself).
vector<int> num;
// diff[node] = len[node] - len[link[node]].
// Used for fast DP (series links).
vector<int> diff;
// seriesLink[node] = the first ancestor (following suffix links)
// where diff[ancestor] != diff[node].
// Used for fast DP.
vector<int> seriesLink;
// The processed string (for debugging / building).
string s;
// The last node added (longest palindromic suffix of the current string).
int last;
// Total number of nodes created.
int sz;
// -----------------------------------------------------------------
// Constructor: initializes the Palindromic Tree with two roots.
// -----------------------------------------------------------------
// PURPOSE:
// Creates an empty Palindromic Tree ready to process characters.
// INPUT:
// None.
// OUTPUT:
// A PalindromicTree object initialized with the two roots.
// TIME COMPLEXITY:
// O(1)
// NOTES:
// - Root 0 (index 0): represents the "empty" palindrome of length -1
// (used for odd-length palindrome construction).
// - Root 1 (index 1): represents the "empty" palindrome of length 0
// (used for even-length palindrome construction).
// - The tree starts with these two roots only.
// - link[0] = 0 (self-loop), link[1] = 0.
// - For any character c, next[0][c] and next[1][c] are initialized to -1.
// ===================================================================
PalindromicTree() {
// Initialize next array for the two roots
next.resize(2);
for (int i = 0; i < 2; ++i) {
next[i].fill(-1);
}
// len[0] = -1 (odd root), len[1] = 0 (even root)
len = {-1, 0};
// link[0] = 0 (self-loop), link[1] = 0
link = {0, 0};
// occ, num, diff, seriesLink for the roots
occ = {0, 0};
num = {0, 0};
diff = {0, 0};
seriesLink = {0, 0};
// last = 1 (the longest palindromic suffix of an empty string is root 1)
last = 1;
// sz = 2 (two roots)
sz = 2;
// processed string is empty
s = "";
}
// -----------------------------------------------------------------
// addChar(char c)
// Adds a new character to the end of the string and updates the tree.
// -----------------------------------------------------------------
// PURPOSE:
// Processes one new character and updates the Palindromic Tree
// to include all palindromic substrings ending at this new position.
// INPUT:
// c: a character (must be in 'a'..'z' for this implementation)
// OUTPUT:
// (void) but updates the internal state of the tree.
// TIME COMPLEXITY:
// O(1) amortized.
// NOTES:
// - This is the core function of the Palindromic Tree.
// - After adding all characters, the tree contains all distinct
// palindromic substrings of the input string.
// - The function handles finding the longest palindromic suffix
// and adding a new node if needed.
// - It updates 'last' to point to the longest palindromic suffix
// of the new string.
// - This implementation works for lowercase English letters only.
// For other character sets, change the array size and mapping.
// ===================================================================
void addChar(char c) {
int cur = c - 'a';
s.push_back(c);
int pos = (int)s.size() - 1;
// Find the largest palindrome that can be extended with c
// We need a palindrome suffix that has c before it.
// getLink(last) returns the node that can be extended.
int curNode = getLink(last);
// If the palindrome already exists, just update its occurrence count
if (next[curNode][cur] != -1) {
last = next[curNode][cur];
occ[last]++;
return;
}
// Create a new node for the new palindrome
int newNode = sz++;
next.push_back({});
next[newNode].fill(-1);
len.push_back(len[curNode] + 2);
occ.push_back(0);
num.push_back(0);
diff.push_back(0);
seriesLink.push_back(0);
// If the new palindrome has length 1, its suffix link is root 1 (even root)
if (len[newNode] == 1) {
link.push_back(1);
} else {
// Otherwise, find the longest proper palindromic suffix
int linkNode = getLink(link[curNode]);
link.push_back(next[linkNode][cur]);
}
// Set diff and series link
diff[newNode] = len[newNode] - len[link[newNode]];
if (diff[newNode] == diff[link[newNode]]) {
seriesLink[newNode] = seriesLink[link[newNode]];
} else {
seriesLink[newNode] = link[newNode];
}
// Connect the new node in the tree
next[curNode][cur] = newNode;
// Update occurrence count for the new node
occ[newNode] = 1;
// Update num (number of palindromic suffixes)
num[newNode] = num[link[newNode]] + 1;
// Update last
last = newNode;
}
// -----------------------------------------------------------------
// getLink(int node)
// Finds the longest palindrome suffix that can be extended.
// This is an internal helper function.
// -----------------------------------------------------------------
// PURPOSE:
// Finds the node representing the longest palindromic suffix of
// the current string that can be extended with the new character.
// INPUT:
// node: starting node (usually 'last').
// OUTPUT:
// Returns the node id that can be extended.
// TIME COMPLEXITY:
// O(1) amortized.
// NOTES:
// - This is an internal function; you don't normally call it directly.
// - It follows suffix links until it finds a palindrome that has
// the same character before it as the character being added.
// - The condition checks: s[pos - len[node] - 1] == s[pos].
// ===================================================================
int getLink(int node) {
int pos = (int)s.size() - 1;
while (true) {
int curLen = len[node];
if (pos - 1 - curLen >= 0 && s[pos - 1 - curLen] == s[pos]) {
break;
}
node = link[node];
}
return node;
}
// -----------------------------------------------------------------
// build(const string& str)
// Builds the Palindromic Tree from a whole string.
// -----------------------------------------------------------------
// PURPOSE:
// Convenience function to build the tree by adding all characters.
// INPUT:
// str: the input string.
// OUTPUT:
// (void) but builds the tree.
// TIME COMPLEXITY:
// O(n) where n = str.size().
// NOTES:
// - This calls addChar() for each character.
// - After building, the tree is ready for all queries.
// - The string is stored internally as 's'.
// ===================================================================
void build(const string& str) {
for (char c : str) {
addChar(c);
}
}
// -----------------------------------------------------------------
// countOccurrences()
// Counts the total occurrences of each palindrome in the string.
// -----------------------------------------------------------------
// PURPOSE:
// Computes the exact number of times each palindrome appears in the
// processed string. This is not just the number of times it was
// created as a new palindrome; it also counts occurrences that are
// included inside larger palindromes.
// INPUT:
// None.
// OUTPUT:
// Updates the 'occ' array for all nodes.
// TIME COMPLEXITY:
// O(sz) where sz is the number of distinct palindromes.
// NOTES:
// - This is a crucial step. Without calling this function, 'occ'
// only counts how many times the palindrome was the *newest*
// palindrome added, not its total occurrences.
// - The algorithm propagates occurrences from longer palindromes
// to their suffix links.
// - After calling this, occ[node] = total occurrences of the
// palindrome represented by 'node'.
// - IMPORTANT: Call this after building the tree and before
// using getOccurrences() or any function that needs total counts.
// ===================================================================
void countOccurrences() {
// Process nodes in reverse order of creation
// (so longer palindromes propagate to shorter suffixes)
for (int i = sz - 1; i >= 2; --i) {
occ[link[i]] += occ[i];
}
}
// -----------------------------------------------------------------
// getOccurrences(int node)
// Returns the total occurrences of a given palindrome.
// -----------------------------------------------------------------
// PURPOSE:
// Returns how many times the palindrome represented by 'node'
// appears in the original string.
// INPUT:
// node: the node id representing a palindrome.
// OUTPUT:
// Returns the total occurrence count as long long.
// TIME COMPLEXITY:
// O(1)
// NOTES:
// - Must call countOccurrences() before using this function.
// - If you don't call countOccurrences(), occ[node] may be incomplete.
// - To find a node for a specific string, you may need to traverse
// the tree (not provided in this simple version).
// ===================================================================
long long getOccurrences(int node) const {
return occ[node];
}
// -----------------------------------------------------------------
// getNodeCount()
// Returns the total number of distinct palindromes in the string.
// -----------------------------------------------------------------
// PURPOSE:
// Returns the number of distinct palindromic substrings.
// INPUT:
// None.
// OUTPUT:
// Returns the number of nodes minus the two roots.
// TIME COMPLEXITY:
// O(1)
// NOTES:
// - The two roots (0 and 1) are not palindromes in the string.
// - So the number of distinct palindromes = sz - 2.
// - This is the count of all unique palindromic substrings.
// ===================================================================
int getNodeCount() const {
return sz - 2;
}
// -----------------------------------------------------------------
// getNumPalindromicSuffixes(int node)
// Returns the number of distinct palindromic suffixes of a palindrome.
// -----------------------------------------------------------------
// PURPOSE:
// Returns how many distinct palindromic suffixes the palindrome
// represented by 'node' has (including itself).
// INPUT:
// node: the node id representing a palindrome.
// OUTPUT:
// Returns the count of distinct palindromic suffixes.
// TIME COMPLEXITY:
// O(1)
// NOTES:
// - Example: for "ababa", the palindromic suffixes are:
// "ababa", "aba", "a" -> so num = 3.
// - This is computed during tree construction.
// ===================================================================
int getNumPalindromicSuffixes(int node) const {
return num[node];
}
// -----------------------------------------------------------------
// isPalindromeExists(const string& p)
// Checks if a given palindrome exists in the tree.
// -----------------------------------------------------------------
// PURPOSE:
// Returns true if the given palindrome p appears in the string.
// INPUT:
// p: a string to check (must be a palindrome; otherwise returns false).
// OUTPUT:
// Returns true if p is a palindrome and exists in the tree.
// TIME COMPLEXITY:
// O(|p|) (traverses the tree from the center outward).
// NOTES:
// - This function assumes p is a palindrome; if not, it returns false.
// - It traverses the tree from the center (odd root for odd length,
// even root for even length) using the 'next' transitions.
// - Works only for lowercase letters.
// ===================================================================
bool isPalindromeExists(const string& p) {
if (p.empty()) return true;
int n = (int)p.size();
// Check if p is a palindrome first.
for (int i = 0, j = n - 1; i < j; ++i, --j) {
if (p[i] != p[j]) return false;
}
// Determine starting root: odd length -> root 0 (len -1), even -> root 1 (len 0)
int startRoot = (n % 2 == 0) ? 1 : 0;
int node = startRoot;
int mid = n / 2;
if (n % 2 == 0) {
// Even length: center is between indices mid-1 and mid.
// Process characters from center outward (i = mid-1 down to 0)
for (int i = mid - 1; i >= 0; --i) {
int c = p[i] - 'a';
if (next[node][c] == -1) return false;
node = next[node][c];
}
} else {
// Odd length: center is index mid.
// Process characters from center down to 0.
for (int i = mid; i >= 0; --i) {
int c = p[i] - 'a';
if (next[node][c] == -1) return false;
node = next[node][c];
}
}
return true;
}
// -----------------------------------------------------------------
// getLPSLength()
// Returns the length of the longest palindromic substring.
// -----------------------------------------------------------------
// PURPOSE:
// Returns the length of the longest palindromic substring in the
// processed string.
// INPUT:
// None.
// OUTPUT:
// Returns the maximum len[node] for all nodes.
// TIME COMPLEXITY:
// O(sz) where sz is the number of nodes.
// NOTES:
// - The longest palindrome is simply the node with the maximum 'len'.
// - This is O(n) in the worst case (number of distinct palindromes).
// - The time is proportional to the number of distinct palindromes,
// which is O(n).
// ===================================================================
int getLPSLength() const {
int maxLen = 0;
for (int i = 2; i < sz; ++i) {
maxLen = max(maxLen, len[i]);
}
return maxLen;
}
// -----------------------------------------------------------------
// getPalindromicSuffixes(int node)
// Returns all distinct palindromic suffixes of a given palindrome.
// -----------------------------------------------------------------
// PURPOSE:
// Returns a vector of all distinct palindromic suffixes of the
// palindrome represented by 'node'.
// INPUT:
// node: the node id representing a palindrome.
// OUTPUT:
// Returns a vector of node ids representing the palindromic suffixes.
// TIME COMPLEXITY:
// O(k) where k is the number of palindromic suffixes.
// NOTES:
// - The suffixes are obtained by following the 'link' pointers.
// - The list includes the node itself and all its suffix links.
// - This is useful for DP problems that need to iterate over
// palindromic suffixes.
// - The suffixes are in decreasing order of length.
// ===================================================================
vector<int> getPalindromicSuffixes(int node) const {
vector<int> suffixes;
int cur = node;
while (cur > 1) {
suffixes.push_back(cur);
cur = link[cur];
}
return suffixes;
}
// -----------------------------------------------------------------
// getPalindromicSuffixesFast(int node)
// Returns palindromic suffixes using series links (advanced).
// -----------------------------------------------------------------
// PURPOSE:
// Returns all distinct palindromic suffixes of the palindrome
// represented by 'node', but using series links for efficiency.
// This is used in DP optimization problems.
// INPUT:
// node: the node id representing a palindrome.
// OUTPUT:
// Returns a vector of node ids representing the palindromic suffixes.
// TIME COMPLEXITY:
// O(k) where k is the number of "series" (groups of equal diff).
// This is much faster than following all suffix links in the worst case.
// NOTES:
// - Series links allow skipping multiple suffix links with the same
// difference in length.
// - This is an advanced concept used for DP problems like:
// "Minimum number of palindromes to partition a string".
// - Not all problems need this, but it's included for completeness.
// - A "series" is a chain of palindromic suffixes where the difference
// in length between consecutive palindromes is constant.
// ===================================================================
vector<int> getPalindromicSuffixesFast(int node) const {
vector<int> suffixes;
int cur = node;
while (cur > 1) {
suffixes.push_back(cur);
// Jump to the next series link
cur = seriesLink[cur];
}
return suffixes;
}
// -----------------------------------------------------------------
// getDistinctPalindromes()
// Returns all distinct palindromic substrings.
// -----------------------------------------------------------------
// PURPOSE:
// Returns a vector of all distinct palindromic substrings
// as strings.
// INPUT:
// None.
// OUTPUT:
// Returns a vector of strings containing all distinct palindromes.
// TIME COMPLEXITY:
// O(total length of all distinct palindromes) in the worst case.
// This can be O(n^2) in the worst case (e.g., "aaaaa...").
// NOTES:
// - This function reconstructs the palindromic strings from the tree.
// - It traverses the tree from the roots.
// - The total length of all distinct palindromes can be O(n^2),
// so use this carefully for very long strings.
// - The function is recursive; be cautious of recursion depth.
// ===================================================================
vector<string> getDistinctPalindromes() const {
vector<string> result;
// We'll use a DFS to traverse the tree.
// Start from roots 0 and 1.
// For odd length palindromes (start from root 0):
function<void(int, string)> dfs = [&](int node, string cur) {
// Add the current palindrome if it's not a root
if (node >= 2) {
result.push_back(cur);
}
for (int c = 0; c < 26; ++c) {
if (next[node][c] != -1) {
// Add the character on both sides
string nextStr = string(1, char('a' + c)) + cur + string(1, char('a' + c));
dfs(next[node][c], nextStr);
}
}
};
// Start from root 0 (odd length palindromes)
for (int c = 0; c < 26; ++c) {
if (next[0][c] != -1) {
string cur = string(1, char('a' + c));
result.push_back(cur);
// Continue from this node
function<void(int, string)> dfsOdd = [&](int node, string curStr) {
for (int nc = 0; nc < 26; ++nc) {
if (next[node][nc] != -1) {
string nextStr = string(1, char('a' + nc)) + curStr + string(1, char('a' + nc));
result.push_back(nextStr);
dfsOdd(next[node][nc], nextStr);
}
}
};
dfsOdd(next[0][c], cur);
}
}
// Start from root 1 (even length palindromes)
for (int c = 0; c < 26; ++c) {
if (next[1][c] != -1) {
string cur = string(1, char('a' + c)) + string(1, char('a' + c));
result.push_back(cur);
function<void(int, string)> dfsEven = [&](int node, string curStr) {
for (int nc = 0; nc < 26; ++nc) {
if (next[node][nc] != -1) {
string nextStr = string(1, char('a' + nc)) + curStr + string(1, char('a' + nc));
result.push_back(nextStr);
dfsEven(next[node][nc], nextStr);
}
}
};
dfsEven(next[1][c], cur);
}
}
return result;
}
// -----------------------------------------------------------------
// minPalindromicPartitions()
// Returns the minimum number of palindromes needed to partition the string.
// -----------------------------------------------------------------
// PURPOSE:
// Computes the minimum number of palindromic substrings needed to
// partition the entire string. (Classic DP problem)
// INPUT:
// None. The string must have been built.
// OUTPUT:
// Returns the minimum number of palindromes in a partition.
// TIME COMPLEXITY:
// O(n log n) or O(n) amortized using series links.
// This implementation uses the series link optimization to achieve
// O(n log n) in practice.
// NOTES:
// - This is an advanced application of the Palindromic Tree.
// - The DP is: dp[i] = min(dp[i], dp[j-1] + 1) for each palindromic
// suffix ending at position i.
// - Series links are used to skip many suffix links with the same diff.
// - This gives O(n log n) time complexity instead of O(n^2).
// - Example: "abac" -> "a", "b", "a", "c" -> 4 palindromes.
// But "aba", "c" -> 2 palindromes! So the answer is 2.
// - This function builds a new tree alongside the DP (so it can be called
// on an already built tree, but it will rebuild it). This is fine for
// typical usage.
// ===================================================================
int minPalindromicPartitions() {
int n = s.size();
if (n == 0) return 0;
// dp[i] = minimum palindromes to partition s[0..i]
vector<int> dp(n + 1, 1e9);
dp[0] = 0;
// We'll rebuild the tree step by step and maintain DP.
PalindromicTree pt;
for (int i = 1; i <= n; ++i) {
pt.addChar(s[i - 1]);
int node = pt.last;
// Traverse palindromic suffixes using series links
int cur = node;
while (cur > 1) {
int lenNode = pt.len[cur];
int linkNode = pt.link[cur];
dp[i] = min(dp[i], dp[i - lenNode] + 1);
// Use series link to jump to the next series
if (pt.diff[cur] == pt.diff[linkNode]) {
cur = pt.seriesLink[cur];
} else {
cur = linkNode;
}
}
}
return dp[n];
}
// -----------------------------------------------------------------
// maxPalindromicSubsequenceLength()
// Returns the length of the longest palindromic subsequence.
// -----------------------------------------------------------------
// PURPOSE:
// Returns the length of the longest palindromic subsequence
// (not necessarily contiguous) in the string.
// INPUT:
// None.
// OUTPUT:
// Returns the length of the longest palindromic subsequence.
// TIME COMPLEXITY:
// O(n^2) using standard DP.
// NOTES:
// - This is NOT a Palindromic Tree specific function.
// - It's included here because it's a common palindrome-related problem.
// - The Palindromic Tree itself does not solve this directly.
// - This is a classic DP problem: lps[i][j] = length of LPS in s[i..j].
// - This function uses O(n^2) time and O(n^2) memory.
// - For very large strings, consider using Manacher or other algorithms.
// ===================================================================
int longestPalindromicSubsequence() const {
int n = s.size();
if (n == 0) return 0;
vector<vector<int>> dp(n, vector<int>(n, 0));
for (int i = 0; i < n; ++i) dp[i][i] = 1;
for (int len = 2; len <= n; ++len) {
for (int i = 0; i + len - 1 < n; ++i) {
int j = i + len - 1;
if (s[i] == s[j]) {
dp[i][j] = dp[i + 1][j - 1] + 2;
} else {
dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);
}
}
}
return dp[0][n - 1];
}
};
// ===================================================================
// EXTRA FUNCTIONS (Not part of the class, but useful standalone)
// ===================================================================
// -----------------------------------------------------------------
// isPalindrome(string s)
// Checks if a string is a palindrome.
// -----------------------------------------------------------------
// PURPOSE:
// Returns true if the given string is a palindrome.
// INPUT:
// s: input string.
// OUTPUT:
// Returns true if s reads the same forwards and backwards.
// TIME COMPLEXITY:
// O(|s|)
// NOTES:
// - A simple two-pointer check.
// - Works for any string.
// ===================================================================
bool isPalindrome(const string& s) {
int l = 0, r = (int)s.size() - 1;
while (l < r) {
if (s[l++] != s[r--]) return false;
}
return true;
}
// -----------------------------------------------------------------
// countPalindromicSubstrings(const string& s)
// Counts the total number of palindromic substrings (not necessarily distinct).
// -----------------------------------------------------------------
// PURPOSE:
// Returns the total number of palindromic substrings in s.
// This includes duplicates (i.e., counts each occurrence separately).
// INPUT:
// s: input string.
// OUTPUT:
// Returns the total count of palindromic substrings.
// TIME COMPLEXITY:
// O(n^2) using the standard DP or O(n) using Manacher.
// NOTES:
// - This function uses the standard O(n^2) DP approach.
// - For O(n) complexity, use Manacher's algorithm.
// - The Palindromic Tree can also be used to count distinct palindromes.
// ===================================================================
long long countPalindromicSubstrings(const string& s) {
int n = s.size();
vector<vector<bool>> dp(n, vector<bool>(n, false));
long long ans = 0;
for (int i = 0; i < n; ++i) {
dp[i][i] = true;
ans++;
}
for (int len = 2; len <= n; ++len) {
for (int i = 0; i + len - 1 < n; ++i) {
int j = i + len - 1;
if (s[i] == s[j] && (len == 2 || dp[i + 1][j - 1])) {
dp[i][j] = true;
ans++;
}
}
}
return ans;
}
// -----------------------------------------------------------------
// countDistinctPalindromicSubstrings(const string& s)
// Counts the number of distinct palindromic substrings using Palindromic Tree.
// -----------------------------------------------------------------
// PURPOSE:
// Returns the number of distinct palindromic substrings in s.
// INPUT:
// s: input string.
// OUTPUT:
// Returns the count of distinct palindromic substrings.
// TIME COMPLEXITY:
// O(n) where n = s.size().
// NOTES:
// - This uses the Palindromic Tree.
// - The answer is simply the number of nodes minus 2.
// ===================================================================
long long countDistinctPalindromicSubstrings(const string& s) {
PalindromicTree pt;
pt.build(s);
return pt.getNodeCount();
}
// -----------------------------------------------------------------
// longestPalindromicSubstringManacher(const string& s)
// Finds the longest palindromic substring using Manacher's algorithm.
// -----------------------------------------------------------------
// PURPOSE:
// Returns the longest palindromic substring of s.
// INPUT:
// s: input string.
// OUTPUT:
// Returns the longest palindromic substring as a string.
// TIME COMPLEXITY:
// O(n)
// NOTES:
// - Manacher's algorithm is an alternative to the Palindromic Tree.
// - It finds the longest palindromic substring in O(n) time and O(n) space.
// - The Palindromic Tree can also find the longest palindrome using getLPSLength(),
// but it doesn't return the string itself directly.
// ===================================================================
string longestPalindromicSubstringManacher(const string& s) {
if (s.empty()) return "";
// Transform the string to insert separators
string t = "#";
for (char c : s) {
t += c;
t += '#';
}
int n = t.size();
vector<int> p(n, 0);
int center = 0, right = 0;
for (int i = 0; i < n; ++i) {
int mirror = 2 * center - i;
if (i < right) {
p[i] = min(right - i, p[mirror]);
}
// Expand around center i
while (i - p[i] - 1 >= 0 && i + p[i] + 1 < n && t[i - p[i] - 1] == t[i + p[i] + 1]) {
p[i]++;
}
if (i + p[i] > right) {
center = i;
right = i + p[i];
}
}
// Find the maximum radius
int maxLen = 0, centerIdx = 0;
for (int i = 0; i < n; ++i) {
if (p[i] > maxLen) {
maxLen = p[i];
centerIdx = i;
}
}
// Extract the longest palindrome
int start = (centerIdx - maxLen) / 2;
return s.substr(start, maxLen);
}
// ===================================================================
// main() with example usage
// ===================================================================
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
// Example 1: Build a Palindromic Tree and count distinct palindromes
string s = "abacaba";
PalindromicTree pt;
pt.build(s);
pt.countOccurrences();
cout << "String: " << s << "\n";
cout << "Number of distinct palindromes: " << pt.getNodeCount() << "\n";
cout << "Length of longest palindrome: " << pt.getLPSLength() << "\n";
// Example 2: Count occurrences of a specific palindrome (by node id)
// In practice, you need to know the node id. Here we just print all.
for (int i = 2; i < pt.sz; ++i) {
cout << "Palindrome node " << i << " occurs " << pt.getOccurrences(i) << " times\n";
}
// Example 3: Minimum palindromic partitions
string s2 = "abac";
PalindromicTree pt2;
pt2.build(s2);
cout << "Minimum palindromic partitions of \"" << s2 << "\": "
<< pt2.minPalindromicPartitions() << "\n";
// Example 4: Count distinct palindromic substrings using standalone function
cout << "Distinct palindromes in \"" << s << "\": "
<< countDistinctPalindromicSubstrings(s) << "\n";
// Example 5: Longest palindromic substring using Manacher
cout << "Longest palindrome in \"" << s << "\": "
<< longestPalindromicSubstringManacher(s) << "\n";
// Example 6: Count total palindromic substrings (including duplicates)
cout << "Total palindromic substrings in \"" << s << "\": "
<< countPalindromicSubstrings(s) << "\n";
return 0;
}
// ===================================================================
// SUMMARY OF ADVANCED TRICKS AND PATTERNS FOR ECPC/ACPC
// ===================================================================
//
// 1. Palindromic Tree Basics:
// - Use the tree to get all distinct palindromes and their frequencies.
// - Always call countOccurrences() after building to get correct counts.
//
// 2. DP with Series Links:
// - Used for problems like "minimum palindromic partitions" in O(n log n).
// - The diff and seriesLink arrays are used to skip many suffix links.
// - Key insight: Palindromic suffixes with the same diff can be grouped.
//
// 3. Palindromic Tree + DP:
// - Many problems require DP on palindromic suffixes.
// - The tree's 'num' array gives the count of palindromic suffixes.
// - The series link optimization is crucial for O(n log n) DP.
//
// 4. Counting Occurrences:
// - After building, propagate counts from longer to shorter palindromes.
// - This is done by iterating nodes in reverse order of creation.
//
// 5. Palindromic Substrings vs Subsequences:
// - Substrings are contiguous; subsequences are not.
// - Palindromic Tree solves substring problems efficiently.
// - Longest Palindromic Subsequence requires standard DP (O(n^2)).
//
// 6. Common ECPC/ACPC Problems:
// - "Number of distinct palindromic substrings" -> simple getNodeCount().
// - "Sum of lengths of all palindromic substrings" -> traverse tree and sum len.
// - "Minimum palindromic partitions" -> DP with series links.
// - "Maximum number of palindromic substrings with constraints" -> DP + tree.
// - "Occurrences of all palindromes" -> countOccurrences().
// - "Longest palindromic substring" -> getLPSLength().
//
// 7. Important Constraints:
// - The Palindromic Tree works for any string length n, O(n) time and memory.
// - The implementation here assumes lowercase English letters ('a'..'z').
// - For larger alphabets, replace array<int,26> with unordered_map<int,int>.
// - The number of distinct palindromes is at most n.
//
// 8. Tricks:
// - To find the node for a specific palindrome, you can traverse the tree.
// - The series link optimization is tricky but powerful.
// - For problems that require building the tree multiple times, reuse the class.
//
// 9. Terms Explained:
// - "Palindrome": a string that reads the same forwards and backwards.
// - "Suffix link": pointer to the longest proper palindromic suffix.
// - "Series link": pointer to the first ancestor with a different diff.
// - "diff": len[node] - len[link[node]].
// - "DP": Dynamic Programming.
// - "O(n log n)": Time complexity, where n is the string length.
//
// 10. When to use Palindromic Tree vs Manacher:
// - Use Palindromic Tree when you need: distinct palindromes, frequencies,
// DP on palindromic suffixes, or any advanced palindrome-related query.
// - Use Manacher when you only need the longest palindromic substring.
// - Manacher is simpler and faster for that specific task.
// ===================================================================