【模板】回文自动机(PAM)
题目背景
模板题,无背景(其实是我想不出背景)。
题目描述
给定一个字符串 sss。保证每个字符为小写字母。对于 sss 的每个位置,请求出以该位置结尾的回文子串个数。
这个字符串被进行了加密,除了第一个字符,其他字符都需要通过上一个位置的答案来解密。
具体地,若第 i(i≥1)i(i\geq 1)i(i≥1) 个位置的答案是 kkk,第 i+1i+1i+1 个字符读入时的 ASCII\rm ASCIIASCII 码为 ccc,则第 i+1i+1i+1 个字符实际的 ASCII\rm ASCIIASCII 码为 (c−97+k) mod 26+97(c-97+k)\bmod 26+97(c−97+k)mod26+97。所有字符在加密前后都为小写字母。
输入格式
一行一个字符串 sss 表示被加密后的串。
输出格式
一行, ∣s∣|s|∣s∣ 个整数。第 iii 个整数表示原串以第 iii 个字符结尾的回文子串个数。
样例 #1
样例输入 #1
debber
样例输出 #1
1 1 1 2 1 1
样例 #2
样例输入 #2
lwk
样例输出 #2
1 1 2
样例 #3
样例输入 #3
lxl
样例输出 #3
1 1 1
提示
对于 100%100\%100% 的数据, 1≤∣s∣≤5×1051\leq |s|\leq 5\times 10^51≤∣s∣≤5×105。
思路:
PAM 经典运用。
时间复杂度:O(n)O(n)O(n)
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
int lstp, tot, fa[N], len[N], cnt[N], ch[N][26];
char s[N];
inline void init()
{
lstp = 0, tot = 1;
fa[0] = 1, fa[1] = len[0] = 0, len[1] = -1;
}
int getfa(int p, int idx)
{
while (idx - len[p] - 1 < 0 || s[idx - len[p] - 1] != s[idx]) {
p = fa[p];
}
return p;
}
void insert(int c, int idx)
{
int p = getfa(lstp, idx);
if (!ch[p][c]) {
int np = ++tot;
int v = getfa(fa[p], idx);
fa[np] = ch[v][c];
len[np] = len[p] + 2;
cnt[np] = cnt[fa[np]] + 1;
ch[p][c] = np;
}
lstp = ch[p][c];
}
signed main()
{
scanf("%s", s);
init();
int last = 0;
for (int i = 0; s[i]; ++i) {
if (i) s[i] = (s[i] - 'a' + last) % 26 + 'a';
insert(s[i] - 'a', i);
last = cnt[lstp];
printf("%d ", last);
}
puts("");
return 0;
}
文章介绍了如何利用PAM(回文自动机)解决一种特殊的字符串问题,其中字符串被加密,每个字符的ASCII码需要根据前一个位置的答案解密。给定一个加密后的字符串,要求计算每个位置结尾的回文子串数量。通过动态规划和回文自动机的方法,可以有效地找到每个位置的答案。
&spm=1001.2101.3001.5002&articleId=130339386&d=1&t=3&u=1357c3819b2d4a448cb616cfae7004e6)
1457

被折叠的 条评论
为什么被折叠?



