链接
题解来源:点击打开链接
给出一个n长的序列
要求序列中最长的子串使得子串满足ABA的形式
B = reverse(A) , 就是A的翻转。
如 1 1 1, 12 21 12, 321 123 321
问:最长的长度是多少
思路:
因为AB就是一个偶数长度的回文,所以显然是先跑一个Manacher
如:
1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10
3 - 2 - 1 - 1 - 2 - 3 - 3 - 2 - 1 - 4 <--input
0 - 0 - 3 - 0 - 0 - 3 - 0 - 0 - 0 - <--dp 表示这个间隙的回文长度
处理出dp数组表示每个数字间隙间的最长回文长度。
考虑间隙 3-4, 若3-4是A - B 部分的间隙,则B - A 部分的间隙可能是 4-5, 5-6, 6-7
假设间隙是j ,那么 j 点的回文长度必须覆盖i点,即:dp[j] >= j - i
变形一下: i >= j - dp[j]
若j能成为答案,则答案为 (j-i)*3, 所以我们要找到满足 i >= j-dp[j] 中最大的j值
因为对于不等号右边是关于j的表达式,所以用线段树维护 j-dp[j]的最小值,每次找区间内满足条件的最右点。
还有一个细节,dp[j] 显然是要>0 的,所以当dp[j] ==0时要让dp[j] = -inf,即 j-dp[j] = inf,防止j点更新答案。。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; template <class T> inline bool rd(T &ret) { char c; int sgn; if (c = getchar(), c == EOF) return false; while (c != '-' && (c < '0' || c > '9')) c = getchar(); sgn = (c == '-') ? -1 : 1; ret = (c == '-') ? 0 : (c - '0'); while (c = getchar(), c >= '0' && c <= '9') ret = ret * 10 + (c - '0'); ret *= sgn; return true; } typedef long long ll; const int MAX_N = 250007; const int inf = 1e8; int n; int str[MAX_N], s[MAX_N << 1]; int p[MAX_N << 1], dp[MAX_N << 1]; #define lson idx << 1 #define rson idx << 1 | 1 struct Node { int l, r; int mi, ma; Node() {} Node(int _l, int _r, int _mi, int _ma) { l = _l, r = _r, mi = _mi, ma = _ma; } }; struct Segment { Node tree[MAX_N << 1]; void up(int idx) { tree[idx].ma = max(tree[lson].ma, tree[rson].ma); tree[idx].mi = min(tree[lson].mi, tree[rson].mi); } void build(int idx, int L, int R) { tree[idx] = Node(L, R, 0, 0); if (L == R) { tree[idx].ma = dp[L]; tree[idx].mi = dp[L]; return; } int mid = (L + R) >> 1; build(lson, L, mid); build(rson, mid + 1, R); up(idx); } int query(int l, int r, int idx, int val) { if (tree[idx].l == tree[idx].r) { return tree[idx].r; } int mid = (tree[idx].l + tree[idx].r) >> 1; if (r <= mid) { if (tree[lson].mi <= val)return query(l, r, lson, val); } else if (mid < l) { if (tree[rson].mi <= val) return query(l, r, rson, val); } else { int lMin = tree[lson].mi, rMin = tree[rson].mi; if (rMin <= val) return query(mid + 1, r, rson, val); else if (lMin <= val) return query(l, mid, lson, val); } return -1; } } Seg; int Manacher() { int cnt = 0; s[cnt++] = -2, s[cnt++] = -1; for (int i = 0; i < n; ++i) { s[cnt++] = str[i]; s[cnt++] = -1; } memset(p, 0, sizeof p); int mx = 0, id = 0; for (int i = 1; i < cnt; ++i) { if (mx > i) p[i] = min(p[2 * id - i], mx - i); else p[i] = 1; while (i - p[i] > 0 && i + p[i] < cnt && s[i - p[i]] == s[i + p[i]]) ++p[i]; if (i + p[i] > mx) { mx = i + p[i]; id = i; } } for (int i = 1; i < cnt; ++i) { if (s[i] == -1) { int id = i >> 1; dp[id] = id - (p[i] - 1) / 2; if ((p[i] - 1) / 2 == 0)dp[id] = inf; } } Seg.build(1, 1, n - 1); int ans = 0; for (int i = 1; i < n; ++i) { if (i - dp[i] >= 1 && i + 1 <= min(i + i - dp[i], n - 1)) ans = max(ans, Seg.query(i + 1, min(i + i - dp[i], n-1), 1, i) - i); } return ans; } int main() { int T; int cas = 0; scanf("%d", &T); while (T-- > 0) { rd(n); for (int i = 0; i < n; ++i) rd(str[i]); int now; if (n < 3)now = 0; else now = Manacher() * 3; printf("Case #%d: %d\n", ++cas, now); } return 0; }