const int N = 3e5 + 5;
struct PAM {
#define KIND 26
int n, last, tot;
int len[N], trie[N][KIND], fail[N], cnt[N], S[N], num[N];
//len[i]: 节点i所代表的回文串长度, fail[i]: 当前回文串的最长回文后缀(不包括自身)
//cnt[i]: 节点i所代表的回文串的个数, S[i]: 第i次添加的字符, num[i]: 以第i个字符为结尾的回文串个数
//last: 上一个字符构成最长回文串的位置,方便下一个字符的插入
//tot: 总结点个数 = 本质不同的回文串的个数+2, n: 插入字符的个数
int newnode(int l) {
f(i, 0, KIND - 1) trie[tot][i] = 0;
cnt[tot] = 0, len[tot] = l, num[tot] = 0;
return tot++;
}
inline void init() {
tot = n = last = 0, newnode(0), newnode(-1);
S[0] = -1, fail[0] = 1;
}
int get_fail(int x) { //获取fail指针
while (S[n - len[x] - 1] != S[n]) x = fail[x];
return x;
}
inline int insert(int c) { //插入字符
c -= 'a';
S[++n] = c;
int cur = get_fail(last);
//在节点cur前的字符与当前字符相同,即构成一个回文串
if (!trie[cur][c]) { //这个回文串没有出现过
int now = newnode(len[cur] + 2);
fail[now] = trie[get_fail(fail[cur])][c];
trie[cur][c] = now;
num[now] = num[fail[now]] + 1; //更新以当前字符为结尾的回文串的个数
}
last = trie[cur][c];
cnt[last]++; //更新当前回文串的个数
return num[last]; //返回以当前字符结尾的回文串的个数
}
void count() { //统计每个本质不同回文串的个数
ff(i, tot - 1, 0) cnt[fail[i]] += cnt[i];
}
}pam;
char s[N];
int main()
{
//freopen("in.txt", "r", stdin);
scanf("%s", s);
pam.init();
int len = strlen(s);
f(i, 0, len - 1)pam.insert(s[i]);
pam.count();
ll mx = 0;
f(i, 0, pam.tot - 1)
mx = max(mx, (ll)pam.len[i] * pam.cnt[i]);
cout << mx << endl;
return 0;
}
P3649 [APIO2014]回文串(回文自动机板子题)
最新推荐文章于 2022-01-28 18:26:04 发布