回文自动机 PAM
https://oi-wiki.org/string/pam/#_4
临时口胡
因为来不及做了,先组一套题目,感受一下题型。
回文自动机(2018ACM-ICPC南京赛区网络赛: I. Skr)
长度为n的数字串,只包含数字1~9,求所有本质不同的回文串代表的整数之和
例如:“1232111”:ans = 1+11+111+2+3+232+12321 = 12681
可以预处前缀和,然后记录每个回文串的位置。
2019杭电多校二 I Love Palindrome String(回文自动机)
给出一个长度为N的字符串,要求输出一个长度为N的统计数组A
A[i]表示长度为i的good substring的数量
good substring 的定义是 该子串是回文串,且该子串的一半也是回文串。
预处理字字符串哈希,遍历所有回文子串云云。
2014 Asia Xian Regional Contest G
给出两个字符串A,B。求A的所有回文串在B中出现次数的和。
可能是对两个树搜索公共部分···
2019牛客暑期多校训练营(第六场)C:Palindrome Mouse(回文树+树剖)
https://blog.csdn.net/cymbals/article/details/98398375
给定字符串Str,求出回文串集合为S,问S中的(a,b)满足a是b的子串的对数。
https://ac.nowcoder.com/acm/contest/886/C
我感觉,或许和链深度有关。
luogu 最长双回文串
输入长度为nnn的串SSS,求SSS的最长双回文子串TTT,即可将TTT分为两部分XXX,YYY,(∣X∣,∣Y∣≥1|X|,|Y|≥1∣X∣,∣Y∣≥1)且XXX和YYY都是回文串。
我觉得是排除掉直接fail连向根的节点去找。
大致意思就是让你求出一个字符串中所有的奇回文串,并把它们的长度前k的长度连乘
由于答案可能很大,输出除以19930726的余数。
马拉车也可做,但是回文树奇根和偶根是独立的,也可以操作的样子。
(但我有奇偶分离的马拉车,其实还好。
记t为s翻转后的串,求一个串最长的形如stst的子串长度。
= = 原来这就是19年杭电多校的原题啊。打扰了。
BestCoder Round #52 (div.1)
给你n次操作,
如果为1,则在字符串后面插入一个字符,
如果为2,则在字符串前面插入一个字符,
如果为3,则输出当前的字符串中的本质不同的回文串的个数,
如果为4,则输出字符串的回文串的个数。
看起来要双向维护pam,这个变形比较需要基本原理,有点难度。
最近的gym 也有一道题。 H. happy game
思考
struct 还是 namespace 这是个问题。
两者各有各的好处。
namespace 的优势在于,如果你的逻辑都在一个模板里改,那么会很方便,但是在外面需要敲范围解析运算符::
。而且在某些情况下,会避免初始化的问题。
struct 的优势在于,要根外部逻辑结合的时候,就一个点符号是比较方便的。而且容易让人去意识到作用域的问题。
我觉得两种方法都有可取的地方。
现场赛的时候甚至可能要直接敲成全局(如果是裸题的话)
2019 ICPC 徐州站 网络赛 G
https://nanti.jisuanke.com/t/41389
题意
设一个回文串的贡献为其包含的不同字符的个数,则给出一个原串,求其所有回文子串的贡献之和。
思路
知道PAM可以遍历所有的回文子串,那么只要得知每个回文子串的位置即可。
这个信息很好维护。
至于求权值可以借助前缀做。
代码
namespace版本
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int PAMN = 3e5 + 59;
namespace PAM {
int tot, las;
int ch[PAMN][26];
int cnt[PAMN];
int len[PAMN];
int fail[PAMN];
int npos[PAMN];
int pos;
char s[PAMN];
int node(int l) {
tot++;
memset(ch[tot], 0, sizeof(ch[tot]));
len[tot] = l;
cnt[tot] = 0;
fail[tot] = 0;
npos[tot] = pos; // 标记回文串第次出现的位置
return tot;
}
void init() {
tot = -1;
las = 0;
pos = 0;
s[pos] = '$'; // s = '$......'
node(0); // node[0] : len = 0 偶根
node(-1); // node[1] : len = -1 奇根
fail[0] = 1;
}
int jump(int u) {
while (s[pos - len[u] - 1] != s[pos]) {
u = fail[u];
}
return u;
}
void extend(char c) {
s[++pos] = c;
int d = c - 'a';
int u = jump(las);
if (!ch[u][d]) {
int v = node(len[u] + 2);
fail[v] = ch[jump(fail[u])][d];
ch[u][d] = v;
}
npos[ch[u][d]] = pos; // 尝试表示每个回文串最后的位置。(删掉也对)
las = ch[u][d];
cnt[las]++;
}
int arr[PAMN][26];
ll solve() {
ll ans = 0;
for (int i = tot; i > 1; i--) {
cnt[fail[i]] += cnt[i];
}
for (int i = 1; i <= pos; ++i) {
memcpy(arr[i], arr[i - 1], sizeof arr[0]);
arr[i][s[i] - 'a']++;
}
for (int i = 2, l, r; i <= tot; i++) {
ll tmp = 0;
r = npos[i];
l = r - len[i];
for (int j = 0; j < 26; ++j) {
tmp += ll(arr[l][j] < arr[r][j]);
}
ans += tmp * cnt[i];
}
return ans;
}
}; // namespace pam
char s[PAMN];
int main() {
PAM::init();
scanf("%s", s + 1);
int n = strlen(s + 1);
for (int i = 1; i <= n; i++) {
PAM::extend(s[i]);
}
printf("%lld\n", PAM::solve());
return 0;
}
struct版本
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int PAMN = 3e5 + 59;
struct PAM {
int tot, las;
int ch[PAMN][26];
int cnt[PAMN];
int len[PAMN];
int fail[PAMN];
int npos[PAMN];
int pos;
char s[PAMN];
int node(int l) {
tot++;
memset(ch[tot], 0, sizeof(ch[tot]));
len[tot] = l;
cnt[tot] = 0;
fail[tot] = 0;
npos[tot] = pos;
return tot;
}
void init() {
tot = -1;
las = 0;
pos = 0;
s[pos] = '$'; // s = '$......'
node(0); // node[0] : len = 0 偶根
node(-1); // node[1] : len = -1 奇根
fail[0] = 1;
}
int jump(int u) {
while (s[pos - len[u] - 1] != s[pos]) {
u = fail[u];
}
return u;
}
void extend(char c) {
s[++pos] = c;
int d = c - 'a';
int u = jump(las);
if (!ch[u][d]) {
int v = node(len[u] + 2);
fail[v] = ch[jump(fail[u])][d];
ch[u][d] = v;
}
las = ch[u][d];
cnt[las]++;
}
} pam;
char s[PAMN];
int arr[PAMN][26];
int main() {
pam.init();
scanf("%s", s + 1);
int n = strlen(s + 1);
for (int i = 1; i <= n; i++) {
pam.extend(s[i]);
}
ll ans = 0;
for (int i = pam.tot; i > 1; i--) {
pam.cnt[pam.fail[i]] += pam.cnt[i];
}
for (int i = 1; i <= n; ++i) {
memcpy(arr[i], arr[i - 1], sizeof arr[0]);
arr[i][s[i] - 'a']++;
}
for (int i = 2, l, r; i <= pam.tot; i++) {
ll tmp = 0;
r = pam.npos[i];
l = r - pam.len[i];
for (int j = 0; j < 26; ++j) {
tmp += ll(arr[l][j] < arr[r][j]);
}
ans += tmp * pam.cnt[i];
}
printf("%lld\n", ans);
return 0;
}