【模板】回文自动机(PAM)
题目背景
模板题,无背景(其实是我想不出背景)。
题目描述
给定一个字符串 s s s。保证每个字符为小写字母。对于 s s s 的每个位置,请求出以该位置结尾的回文子串个数。
这个字符串被进行了加密,除了第一个字符,其他字符都需要通过上一个位置的答案来解密。
具体地,若第 i ( i ≥ 1 ) i(i\geq 1) i(i≥1) 个位置的答案是 k k k,第 i + 1 i+1 i+1 个字符读入时的 A S C I I \rm ASCII ASCII 码为 c c c,则第 i + 1 i+1 i+1 个字符实际的 A S C I I \rm ASCII ASCII 码为 ( c − 97 + k ) m o d 26 + 97 (c-97+k)\bmod 26+97 (c−97+k)mod26+97。所有字符在加密前后都为小写字母。
输入格式
一行一个字符串 s s s 表示被加密后的串。
输出格式
一行, ∣ s ∣ |s| ∣s∣ 个整数。第 i i i 个整数表示原串以第 i i i 个字符结尾的回文子串个数。
样例 #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 × 1 0 5 1\leq |s|\leq 5\times 10^5 1≤∣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;
}