题解:P4555

link

题目大意

求一个字符串 \(S\) 的两个回文子串 \(X,Y\),要求 \(X\)\(Y\) 要相邻,求最长的 \(|X|+|Y|\)

前置知识

双指针、马拉车

solution

显然要求出一个数组 \(p\),其中 \(p_i\) 表示以 \(i\) 结尾的最长回文串的长度。

然后正反做两遍这个 \(p\),拼起来就行了。

具体怎么求 \(p\) 数组,这里大佬们可以直接使用回文自动机,但是作为蒟蒻的我只会 manacher,怎么办!!!

这里提供一种双指针+manacher的解法。

设指针 \(i,j\) 表示目前枚举到 \(j\) 这个位置,目前的最长回文串的回文中心是 \(i\)

发现 \(i\) 具有单调性!!!

  • $i \to i + 1 $,如果能扩展到 \(i - p[i]\) ~ \(i + 1\) 那么就扩展。
  • 如果不行那就 \(j \to j+1\) 直到 \(j - 2 * (j - i) + 1\) ~ \(j\) 是回文串。

证明:因为如果 回文中心 i + 1 = 回文中心 i - 0.5 或 - 1,那么 回文中心 i 就一定可以再 - 1

注意由于有偶回文串,所以这个 \(j\) 可以是小数

Code

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int n;
char a[N], t[N], b[N];
int f[N], g[N], p[N];
inline int val (double x) {return p[(int)(x * 2)] - 1;}
inline void solve (char b[], int* d) {
	memset (p, 0, sizeof (p));
	int cnt = 0;
	a[cnt] = '*';
	a[++ cnt] = '#';
	for (int i = 1; i <= n; ++ i) {
		a[++ cnt] = b[i];
		a[++ cnt] = '#';
	}
	int mx = 0, id = 0;
	for (int i = 1; i <= cnt; ++ i) {
		if (i <= mx) p[i] = min (mx - i + 1, p[2 * id - i]);
		while (a[i - p[i]] == a[i + p[i]]) ++ p[i];
		if (p[i] + i - 1 > mx) mx = p[i] + i - 1, id = i;
	}
	double i = 1;
	for (int j = 1; j <= n; ++ j) {
		d[j] = (j - i) * 2 + 1;
		while (val (i) < ((j + 1) - i) * 2 + 1) i += 0.5;
	}
	return ;	
}
signed main () {
	ios::sync_with_stdio (false);
	cin.tie (nullptr), cout.tie (nullptr);
	cin >> (t + 1);
	n = strlen (t + 1);
	solve (t, f);
	reverse (t + 1, t + 1 + n);
	solve (t, g);
	int ans = 0;
	for (int i = 1; i < n; ++ i) ans = max (ans, f[i] + g[n - i]);
	cout << ans << "\n";
	return 0;
}

posted @ 2025-04-11 15:37  FChang  阅读(143)  评论(0)    收藏  举报