[BZOJ 3676][APIO 2014] 回文串

题目大意

考虑一个只包含小写拉丁字母的字符串 $s$。我们定义 $s$ 的一个子串 $t$ 的「出现值」为 $t$ 在 $s$ 中的出现次数乘以 $t$ 的长度。请你求出 $s$ 的所有回文子串中的最 大出现值。

$1 \leqslant |s| \leqslant 300,000$

题目链接

BZOJ 3676

题解

回文树模版。(人傻自带大常数系列。。。)

代码

调用 strlen() 时一定要记得 #include <cstring> 。。。(本机是给过的。。。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <new>
const int MAXN = 300005;
const int CHAR_SET = 'z' - 'a' + 1;
const char BASE_CHAR = 'a';
struct PalinT {
int str[MAXN];
int size;
struct Node {
int len, cnt;
Node *ch[CHAR_SET], *fail;
Node(int len = 0) : len(len), cnt(0) {
for (int i = 0; i < CHAR_SET; i++) ch[i] = NULL;
}
} *root[2], *last, nodes[MAXN], *curr;
PalinT() {
curr = nodes;
root[0] = last = new (curr++) Node(0);
root[1] = last->fail = new (curr++) Node(-1);
root[1]->fail = root[1];
str[size = 0] = -1;
}
Node *getFail(Node *u) {
while (str[size - u->len - 1] != str[size]) u = u->fail;
return u;
}
void insert(int c) {
str[++size] = c;
Node *o = getFail(last);
if (!o->ch[c]) {
Node *u = (o->ch[c] = new (curr++) Node(o->len + 2));
u->fail = o == root[1] ? root[0] : getFail(o->fail)->ch[c];
}
last = o->ch[c];
last->cnt++;
}
void build(char *s) {
int n = strlen(s);
for (int i = 0; i < n; i++) insert(s[i] - BASE_CHAR);
}
void count() {
Node *p = curr - 1;
for (; p != nodes; p--) p->fail->cnt += p->cnt;
p->fail->cnt += p->cnt;
}
} palinT;
int main() {
static char s[MAXN];
scanf("%s", s);
palinT.build(s);
palinT.count();
long long ans = 0;
for (PalinT::Node *p = palinT.nodes; p != palinT.curr; p++)
ans = std::max(ans, (long long) p->len * p->cnt);
printf("%lld\n", ans);
return 0;
}