回文树(Palindromic Tree)
最基本的回文树在网上用很多资料,在这里做简单的介绍。本文的重点是后面回文树的一些更广泛的应用。
注:本文的图片转自http://adilet.org/blog/25-09-14/,所以图中某些变量的定义可能与本文不同,需要注意。
定义
节点
回文树中的每个节点都对应了一个回文串。
特别注意在回文树中还有两个特殊的节点分别对应了空串和长度为 −1 的串,是为了添加只有一个字符和两个字符的回文串时更方便,后面会提到。
有个结论是:一个字符串
s
本质不同的回文串个数是
证明:利用数学归纳发证明这个结论。
1.对于
|s|=1
的字符串,回文串的数量为1,结论成立。
2.对于
|s|>1
的字符串,假设
s
是在
毕证。
边
在回文树中有两种边,一种是字符边,一种是对应后缀的 fail 边。
字符边
在回文树中假如一个节点
a
对应的字符串可以通过在前面和后面分别添加一个字符
fail 边
在回文树中假如一个节点
a
对应的字符串的最长回文后缀是节点
构造
构造由于比较简单就简略的讲一下…
考虑每次在字符串
s
的末尾添加一个新的字符
设原来最长的后缀回文串对应的节点是
a
,那么相当于要不断沿
假如我们新添加了一个节点,那么就要考虑它的
例题【APIO2014】回文串
题目大意:考虑一个只包含小写拉丁字母的符串
s
。我们定义
裸题,直接在加入节点是顺便统计一下个数,最后按
//YxuanwKeith
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 3e5 + 5;
typedef long long LL;
struct Tree {int Len, Fail, Go[26];} Tr[MAXN];
char S[MAXN];
int len, Last, tot, i, Cnt[MAXN];
void InitTree() {
Tr[0].Len = 0, Tr[1].Len = -1;
tot = 1, Last = 0;
Tr[0].Fail = 1;
}
int Get(int Now) {
while (S[i - Tr[Now].Len - 1] != S[i]) Now = Tr[Now].Fail;
return Now;
}
void Add(int c) {
int t = Get(Last);
if (!Tr[t].Go[c]) {
Tr[++ tot].Len = Tr[t].Len + 2;
Tr[tot].Fail = Tr[Get(Tr[t].Fail)].Go[c];
Tr[t].Go[c] = tot;
}
Last = Tr[t].Go[c];
Cnt[Last] ++;
}
LL GetAns() {
LL Ans = 0;
for (int i = tot; i > 1; i --) {
Cnt[Tr[i].Fail] += Cnt[i];
Ans = max(Ans, LL(Tr[i].Len) * LL(Cnt[i]));
}
return Ans;
}
int main() {
freopen("palindrome.in", "r", stdin), freopen("palindrome.out", "w", stdout);
scanf("%s", S + 1);
len = strlen(S + 1);
InitTree();
for (i = 1; i <= len; i ++) Add(S[i] - 'a');
printf("%lld", GetAns());
}
黑科技
预备队大佬不让我写…只能在预备队互测完才能放出来…