【luogu P3649】【UOJ #103】回文串 / Palindromes(PAM)

回文串 / Palindromes

题目链接:luogu P3649 / UOJ #103

题目大意

给你一个字符串,要你找一个分数最大的回文子串。
一个子串的分数是它的长度乘它在字符串中出现的次数。

思路

看到回文串想到 Manacher 和 PAM。
然后发现要统计回文串出现次数,自然想到 Manacher 的 n u m num num 数组。
然后就求出来,然后每个暴力看取最大值就可以了。

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long

using namespace std;

struct PAM {
	int nxt[26], trans;
	int num, sum, fail, len;
}t[300002];
int sn, lst, tot;
char s[300001];
ll ans;

int get_new(int l) {
	t[++tot].len = l;
	for (int i = 0; i < 26; i++) t[tot].nxt[i] = 0;
	t[tot].num = t[tot].sum = t[tot].fail = t[tot].trans = 0;
	return tot;
}

int get_fail(int x, int pl) {
	while (s[pl - t[x].len - 1] != s[pl]) x = t[x].fail;
	return x;
}

void count() {
	for (int i = tot; i >= 2; i--)
		t[t[i].fail].num += t[i].num;
}

int main() {
	scanf("%s", s + 1);
	sn = strlen(s + 1);
	
	tot = 1; lst = 0;
	t[0].fail = 1; t[1].fail = 0;
	t[0].len = 0; t[1].len = -1;
	for (int i = 1; i <= sn; i++) {
		int pre = get_fail(lst, i), go = s[i] - 'a';
		if (!t[pre].nxt[go]) {
			int now = get_new(t[pre].len + 2);
			t[now].fail = t[get_fail(t[pre].fail, i)].nxt[go];
			t[pre].nxt[go] = now;
			t[now].sum = t[t[now].fail].sum + 1;
			if (t[now].len <= 2) t[now].trans = t[now].fail;
				else {
					int tmp = t[pre].trans;
					while (s[i - t[tmp].len - 1] != s[i] || ((t[tmp].len + 2) << 1) > t[now].len)
						tmp = t[tmp].fail;
					t[now].trans = t[tmp].nxt[go];
				}
		}
		lst = t[pre].nxt[go];
		t[lst].num++;
	}
	
	count();
	for (int i = 2; i <= tot; i++)
		ans = max(ans, 1ll * t[i].num * t[i].len);
	printf("%lld", ans);
	
	return 0;
}
上一篇:I'm in Luogu


下一篇:luogu P2597 [ZJOI2012]灾难