1.回文树上每一个节点代表了原串上出现过的一个本质不同回文子串,原串上的每一个回文子串都在回文树上有对应。回文树上每一个点代表的串都是回文串。
2.回文树分两部分,奇和偶,奇树上的点代表的回文串长度为奇数,偶树上的为偶树
3.儿子节点代表串长度为父亲节点代表串长度+2
4.和\(Trie\)相似的其他性质,在回文树中,父亲与儿子的连边上的字母代表了往父亲回文串首尾加上这个字母形成儿子所代表的回文串。
Fail指针学过AC自动机的OIer们应该就很熟悉啦QwQ
\(Fail\)指针含义:这个节点所代表的回文串的最长回文后缀
Trans指针一般做许多\(PAM\)题目常用的东西
\(Trans\)指针含义:小于等于当前节点长度一半的最长回文后缀
struct PAM {
int cnt;
int len[100001], num[100001], fail[100001], tree[100001][27], fa[100001], trans[100001];
void clear() {
memset(len, 0, sizeof(len));
memset(num, 0, sizeof(num));
memset(fail, 0, sizeof(fail));
memset(tree, 0, sizeof(tree));
memset(fa, 0, sizeof(fa));
memset(trans, 0, sizeof(trans));
cnt = 1;
fail[0] = 1;//跳到奇根上,即奇数回文串
len[1] = -1;//使得一开始的长度为1
}
int getFail(int p, int i) {
while (tmp[i - len[p] - 1] != tmp[i] || i - len[p] - 1 < 0)//不断地跳最长的回文后缀
p = fail[p];
return p;
}
int getTrans(int p, int i) {
while (tmp[i - len[p] - 1] != tmp[i] || (len[p] + 2 << 1) > len[cnt])//类似fail,只是长度要求
p = fail[p];
return p;
}
void insert(int u, int i) {
int Fail = getFail(last, i);
if (!tree[Fail][u]) {//类似Trie操作
len[++cnt] = len[Fail] + 2;
fail[cnt] = tree[getFail(fail[Fail], i)][u];//次短最长回文后缀
tree[Fail][u] = cnt;
num[cnt] = num[fail[cnt]] + 1;
fa[cnt] = Fail;
if (len[cnt] <= 2)
trans[cnt] = fail[cnt];//特判,防死循环
else
trans[cnt] = tree[getTrans(trans[Fail], i)][u];
}
last = tree[Fail][u];
}
};