【UOJ #103】【APIO 2014】Palindromes

时间:2020-12-03 06:36:12

http://uoj.ac/problem/103

由manacher得:本质不同的回文串只有\(O(n)\)个。

用manacher求出所有本质不同的回文串,对每个本质不同的回文串,在后缀自动机的parent树上倍增求一下它出现了多少次,更新答案。

时间复杂度\(O(n\log n)\)。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll; const int N = 300003; char s[N], r[N << 1];
int n, tot = 0, top = -1, endtot = 0; struct State {
State *par, *go[26], *fa[21];
int val, sum;
} *root, *last, pool[N << 2], *endpos[N]; State *newState(int num) {
State *t = &pool[++top];
t->val = num;
t->par = 0; t->sum = 0;
memset(t->go, 0, sizeof(t->go));
return t;
} void extend(int w) {
State *p = last;
State *np = newState(p->val + 1);
while (p && p->go[w] == 0)
p->go[w] = np, p = p->par;
if (p == 0) np->par = root;
else {
State *q = p->go[w];
if (q->val == p->val + 1) np->par = q;
else {
State *nq = newState(p->val + 1);
memcpy(nq->go, q->go, sizeof(q->go));
nq->par = q->par; q->par = np->par = nq;
while (p && p->go[w] == q)
p->go[w] = nq, p = p->par;
}
}
endpos[++endtot] = last = np;
last->sum = 1;
} int len[N << 1], id[N << 2]; ll cal(int t, int l) {
State *p = endpos[t];
for (int i = 20; i >= 0; --i)
if (p->fa[i]->val >= l)
p = p->fa[i];
return 1ll * p->sum * l;
} bool cmp(int x, int y) {return pool[x].val < pool[y].val;} int main() {
scanf("%s", s + 1);
n = strlen(s + 1);
root = last = newState(0);
for (int i = 1; i <= n; ++i)
extend(s[i] - 'a'); r[0] = '#';
for (int i = 1; i <= n; ++i) {
r[++tot] = '$';
r[++tot] = s[i];
}
r[++tot] = '$';
r[++tot] = '&'; root->par = &pool[top + 1]; root->par->fa[0] = root->par; root->par->val = -1;
for (int i = 0; i <= top; ++i) pool[i].fa[0] = pool[i].par;
for (int j = 1; j <= 20; ++j)
for (int i = 0; i <= top + 1; ++i)
pool[i].fa[j] = pool[i].fa[j - 1]->fa[j - 1]; for (int i = 0; i <= top; ++i) id[i] = i;
stable_sort(id, id + top + 1, cmp);
for (int i = top; i >= 0; --i)
pool[id[i]].par->sum += pool[id[i]].sum; ll ans = 0;
int cur = 0, pos; len[0] = 1;
for (int i = 1; i < tot; ++i) {
int &now = len[i]; pos = (cur << 1) - i; now = 0;
now = min(len[pos], cur + len[cur] - i); now = max(now, 0);
while (r[i - now] == r[i + now]) {
++now;
if ((i + now - 1) & 1) continue;
ans = max(ans, cal((i + now - 1) >> 1, now));
}
if (i + now > cur + len[cur]) cur = i;
} printf("%lld\n", ans); return 0;
}