【UOJ #103】【APIO 2014】Palindromes

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;
}
posted @ 2017-04-17 09:47  abclzr  阅读(257)  评论(0编辑  收藏  举报