Appearance
又称作:回文自动机、回文树。
与 Manacher 不同,PAM 用最长回文后缀的方式描述一个字符串的回文子串。
PAM 是一个树形结构,PAM 上一个一个节点描述一个回文子串。令当前节点为
当在 PAM 中加入
和 Manacher 类似的,偶数长度的回文子串需要和奇数长度的回文子串分类讨论,不区分的话,PAM 上同一个
和 ACAM 类似地,跳 Border 匹配时,可以使用 Trie 图优化,时间复杂度和 ACAM 分析相同。
朴素跳 Border,时间复杂度:
cpp
struct PAM {
int son[N][26], border[N], len[N];
int num[N], cnt[N];
int idx;
void init() {
for (int i = 0; i <= idx; i++)
for (int j = 0; j < 26; j++)
son[i][j] = 0;
for (int i = 0; i <= idx; i++)
border[i] = len[i] = 0;
len[1] = -1;
idx = 1;
border[0] = 1;
}
void build(string &s) {
int n = s.size() - 1;
int cur = 0;
for (int i = 1; i <= n; i++) {
auto getborder = [&](int x) -> int {
while (s[i - 1 - len[x]] != s[i])
x = border[x];
return x;
};
int c = s[i] - 'a';
cur = getborder(cur);
if (!son[cur][c]) {
idx++;
len[idx] = len[cur] + 2;
border[idx] = son[getborder(border[cur])][c];
son[cur][c] = idx;
}
cur = son[cur][c];
}
}
};补全 Trie 图,时间复杂度:
cpp
int c = s[i] - 'a';
int pos = s[(i - 1) - len[cur]] == s[i] ? cur : trans[cur][c];
if (son[pos][c])
cur = son[pos][c];
else {
son[pos][c] = ++idx;
cur = idx;
nex[cur] = pos == 1 ? 0 : son[trans[pos][c]][c];
len[cur] = len[pos] + 2;
num[cur] = num[border[cur]] + 1;
for (int j = 0; j < 26; j++) {
if (len[cur] == 1)
trans[cur][j] = j == c ? 0 : 1;
else
trans[cur][j] = s[i - len[border[cur]]] - 'a' == j
? border[cur]
: trans[border[cur]][j];
}
}本质不同回文子串:
字符串的所有回文子串都能在 PAM 上找到,而 PAM 的节点数最多只有
因为这个性质,所以事实上在不强制在线增量维护回文子串时,可以使用回文中心
具体而言,枚举回文中心,从最长回文半径开始向内收缩,若当前收缩到的位置是已经出现的,则结束收缩。在这个过程中,收缩的次数等于本质不同回文子串的数量,而判断回文子串是否出现过可以使用 Hash 判断,对于 Hash 映射后的整数 ,若使用 map 维护,整个过程的时间复杂度是
在题目不严格卡
Palindrome Series:
用于优化枚举回文后缀转移的 dp。朴素枚举的时间复杂度是
通常而言,此题 dp 问题的 dp 式子形如:
具体而言:
对于
关于
令
因为回文后缀的特殊性质,对于一个回文串的最大回文后缀同时也是这个回文的 Border,所以
注意特判
cpp
void build(string &s) {
int cur = 0, pos;
int n = s.size() - 1;
for (int i = 1; i <= n; i++) {
int c = s[i] - 'a';
pos = s[(i - 1) - len[cur]] == s[i] ? cur : trans[cur][c];
if (son[pos][c])
cur = son[pos][c];
else {
son[pos][c] = ++idx;
cur = idx;
border[cur] = pos == 1 ? 0 : son[trans[pos][c]][c];
int fa = border[cur];
len[cur] = len[pos] + 2;
/*
diff[cur] = len[cur] - len[fa];
slink[cur] = diff[fa] == diff[cur] ? slink[fa] : fa;
*/
for (int j = 0; j < 26; j++) {
if (len[cur] == 1)
trans[cur][j] = j == c ? 0 : 1;
else
trans[cur][j] = s[i - len[border[cur]]] - 'a' == j
? border[cur]
: trans[border[cur]][j];
}
}
}
}对 dp 式:
cpp
f[0] = 1;
for (int i = 1, j; i <= n; i++) {
for (j = a[i]; j > 1; j = slink[j]) {
int dlt = len[slink[j]] + diff[j];
g[j] = f[i - dlt];
if (border[j] != slink[j])
g[j] = (g[j] + g[border[j]]) % mod;
if (i % 2 == 0)
f[i] = (f[i] + g[j]) % mod;
}
}