[国家集训队] 最长双回文串 题解
题目分析
假设 \(x, y\) 都是奇回文串,考虑怎么做:枚举 \(x, y\) 的回文中心 \(i, j\),则必须满足 \(d_i + d_j \ge j - i + 1\),对答案的贡献为 \(2 (j - i)\)。这个东西明显是可以维护的,具体怎么维护我们先不管。
现在考虑 \(x, y\) 中有偶回文串的情况。我们希望能避免复杂的分类讨论,因此尝试在相邻两个字符之间插入分隔符,这样 \(i, j\) 都一定是整数。画一下图可以发现确实不需要分类讨论:条件仍然是 \(d_i + d_j \ge j - i + 1\),但对答案的贡献变成了 \(j - i\)。但是有一个小问题,就是 \(x, y\) 必须非空,因此如果 \(s_i\) 是分隔符的话就必须满足 \(d_i > 1\)。有一个稍微好写的处理方式:我们在串的开头结尾同样插入分隔符,这样如果 \(s_i\) 不是分隔符那么一定有 \(d_i > 1\),因此我们只要判 \(d_i > 1\) 即可。
现在考虑如何维护:限制形如一个二维偏序:\(i < j\),且 \(d_i + d_j \ge j - i + 1\)(移项得 \(i + d_i \ge j - d_j + 1\))。根据 zyh 理论,我们先考虑是否有一维是没用的。因为答案是对 \(j - i\) ckmax,所以当 \(i \ge j\) 时更新不会影响答案,这样我们只剩下一维偏序,排序 + 双指针即可。但是注意到值域只有 \(O(n)\),因此可以直接开桶。
时间复杂度 \(O(n)\)。
代码实现
#include <bits/stdc++.h>
#define FOR(i, l, r) for (int i = l; i < (r); ++i)
#define F0R(i, r) FOR (i, 0, r)
#define ROF(i, l, r) for (int i = r; i-- > (l);)
#define R0F(i, r) ROF (i, 0, r)
#define rep(n) F0R (_, n)
#define each(i, x) for (auto& i : x)
using namespace std;
using ll = long long;
using db = long double;
using str = string;
using pi = pair<int, int>;
#define mp make_pair
#define f first
#define s second
#define ttt template <typename T
ttt > using vec = vector<T>;
ttt, size_t n > using arr = array<T, n>;
using vi = vec<int>;
using vb = vec<bool>;
using vl = vec<ll>;
using vpi = vec<pi>;
#define sz(x) int((x).size())
#define all(x) begin(x), end(x)
#define rsz resize
#define pb push_back
#define eb emplace_back
#define il inline
ttt > il bool ckmin(T& x, const T& y) { return y < x ? x = y, true : false; }
ttt > il bool ckmax(T& x, const T& y) { return x < y ? x = y, true : false; }
int main() {
cin.tie(nullptr)->sync_with_stdio(false), cin.exceptions(cin.failbit);
str s;
cin >> s;
str t = "#";
each (i, s) t += i, t += "#";
int n = sz(t);
vi d(n);
for (int i = 0, s, r = -1; i < n; ++i) {
if (i <= r && d[s - i] <= r - i)
d[i] = d[s - i];
else {
d[i] = max(0, r - i + 1);
while (d[i] <= min(i, n - i - 1) && t[i - d[i]] == t[i + d[i]]) ++d[i];
if (ckmax(r, i + d[i] - 1)) s = 2 * i;
}
}
vi mx(2 * n, -1e9), mn(2 * n, 1e9);
F0R (i, n)
if (d[i] > 1) ckmax(mx[i - d[i] + n], i), ckmin(mn[i + d[i] + n - 1], i);
int cur = 0, ans = 0;
F0R (i, 2 * n) ckmax(cur, mx[i]), ckmax(ans, cur - mn[i]);
cout << ans << "\n";
}