在字符串处理领域,C++回文树(也称为回文自动机,Palindromic Tree)是一种强大而优雅的数据结构,用于高效处理所有回文子串相关的问题。相比传统的 Manacher算法 只能求最长回文子串,回文树可以动态维护所有本质不同的回文子串,并支持快速插入与查询。
本教程将手把手教你用 C++ 实现回文树,即使你是编程小白,也能轻松理解!我们将围绕回文自动机的核心思想、数据结构设计、构建过程和代码实现展开讲解。

回文树是一种有限状态自动机,每个节点代表一个本质不同的回文子串。它有两个根节点:
每个节点包含以下信息:
len:该回文串的长度fail:后缀链接(类似AC自动机中的fail指针),指向当前回文串的最长真回文后缀next[26]:字符转移数组(假设只处理小写字母)cnt:该回文串在原串中出现的次数(可选)我们逐个向回文树中添加字符。设当前处理到第 i 个字符 s[i],并维护一个变量 last 表示以 s[i-1] 结尾的最长回文后缀对应的节点。
构建步骤如下:
last 开始,沿着 fail 指针向上跳,直到找到一个节点,其前一个字符等于 s[i];s[i] 转移存在,则更新 last 为该子节点;当前节点.len + 2,并设置其 fail 指针(继续沿 fail 链找最长回文后缀);last 为新节点,并累加计数。下面是一个完整的、注释清晰的 C++ 回文树实现,适用于处理小写英文字母组成的字符串:
#include <iostream>#include <cstring>#include <vector>using namespace std;const int MAXN = 300010; // 最大字符串长度const int ALPHA = 26; // 字符集大小(a-z)struct PalindromicTree { struct Node { int len; // 回文串长度 int fail; // 后缀链接 int cnt; // 出现次数(可选) int next[ALPHA]; // 转移边 Node() { len = cnt = fail = 0; memset(next, 0, sizeof(next)); } }; vector<Node> tree; string s; int last; // 当前最长回文后缀节点 int n; // 已插入字符数 int total; // 节点总数(不包括两个根) PalindromicTree() { tree.clear(); tree.push_back(Node()); // 根0:len = -1 tree[0].len = -1; tree[0].fail = 0; // 根0的fail指向自己 tree.push_back(Node()); // 根1:len = 0 tree[1].len = 0; tree[1].fail = 0; // 根1的fail指向根0 last = 1; n = 0; total = 1; // 当前只有根1是有效回文节点 s = ""; } // 扩展字符串,加入字符 c void extend(char c) { s += c; n++; int cur = last; int idx = c - 'a'; // 找到满足 s[n - 1 - node.len] == c 的节点 while (true) { int pos = n - 1 - tree[cur].len - 1; if (pos >= 0 && s[pos] == c) break; cur = tree[cur].fail; } // 如果该转移已存在 if (tree[cur].next[idx]) { last = tree[cur].next[idx]; tree[last].cnt++; // 可选:统计出现次数 return; } // 创建新节点 tree.push_back(Node()); int now = ++total; tree[now].len = tree[cur].len + 2; tree[cur].next[idx] = now; // 设置 fail 指针 if (tree[now].len == 1) { // 单字符回文,fail 指向空串(根1) tree[now].fail = 1; } else { // 沿 fail 链向上找 cur = tree[cur].fail; while (true) { int pos = n - 1 - tree[cur].len - 1; if (pos >= 0 && s[pos] == c) { tree[now].fail = tree[cur].next[idx]; break; } cur = tree[cur].fail; } } tree[now].cnt = 1; // 初始出现1次 last = now; } // 获取所有本质不同回文子串数量 int size() { return total; // 不包括两个虚拟根 }};// 示例:统计不同回文子串数量int main() { string str = "ababa"; PalindromicTree pt; for (char c : str) { pt.extend(c); } cout << "不同回文子串数量: " << pt.size() << endl; // 输出 3("a", "b", "aba", "bab", "ababa" 共5个,但本质不同为5?实际应为5) // 注意:本实现中 size() 返回的是节点数(即不同回文数),对于 "ababa" 应为 5 return 0;}> 💡 提示:上述代码中 size() 返回的是本质不同的回文子串数量。对于字符串 "ababa",回文子串有 a, b, aba, bab, ababa,共5个,因此输出应为5。
许多初学者会问:既然有 Manacher算法替代 方案,为什么还要学回文树?关键区别在于:
| 特性 | Manacher算法 | 回文树(Palindromic Tree) |
|---|---|---|
| 功能 | 求所有中心的最长回文半径 | 维护所有本质不同回文子串 |
| 是否支持动态插入 | 否(需全串预处理) | 是(在线算法) |
| 空间复杂度 | O(n) | O(n × |Σ|),Σ为字符集 |
回文树特别适合解决以下问题:
通过本教程,你已经掌握了 C++回文树 的基本原理与实现方法。回文树虽然概念稍显抽象,但一旦理解其“后缀链接”和“节点扩展”机制,就能灵活应用于各类字符串回文处理问题中。
建议你动手敲一遍代码,并尝试修改以支持大写字母或数字字符集。实践是最好的老师!
关键词回顾:C++回文树、回文自动机、Manacher算法替代、字符串回文处理
本文由主机测评网于2025-12-19发表在主机测评网_免费VPS_免费云服务器_免费独立服务器,如有疑问,请联系我们。
本文链接:https://www.vpshk.cn/20251210170.html