牛客-回文串
题目传送门
sol:根据题意,可以把字符串截成两段,要求两段中最大的两个回文串相加最大的方案,于是跑马拉车维护一个前缀,再倒着跑马拉车维护后缀。因为最优方案肯定是在两个字母之间截,所以最后只考虑在'#'位置截断的情况
- 马拉车
#include <bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int, int> PII; const int MAXN = 2e5 + 10; char s[MAXN * 2]; int p[MAXN * 2]; int pre[MAXN * 2], suf[MAXN * 2]; int n, k; void manacher() { k = 0; memset(p, 0, sizeof(p)); for (int i = 1; i < n; i++) { if (i < p[k] + k) { p[i] = min(k + p[k] - i, p[2 * k - i]); } while (s[i + p[i]] == s[i - p[i]]) { p[i] ++; pre[i + p[i] - 1] = max(pre[i + p[i] - 1], p[i] - 1); } pre[i] = max(pre[i], pre[i - 1]); } k = n; memset(p, 0, sizeof(p)); for (int i = n - 1; i > 0; i--) { if (i > p[k] - k) { p[i] = max(0, min(i - k + p[k], p[2 * k - i])); } while (s[i + p[i]] == s[i - p[i]]) { p[i] ++; suf[i - p[i] + 1] = max(suf[i - p[i] + 1], p[i] - 1); } suf[i] = max(suf[i], suf[i + 1]); } } int main() { scanf("%s", s); n = strlen(s); for (int i = n; i >= 0; i--) { s[i + 1 << 1] = s[i]; s[i << 1 | 1] = '#'; } s[0] = '$'; n = n + 1 << 1; manacher(); /* puts(s); for (int i = 0; i < n; i++) printf("%x", p[i]); cout << endl; for (int i = 0; i < n; i++) printf("%x", pre[i]); cout << endl; for (int i = 0; i < n; i++) printf("%x", suf[i]); cout << endl; */ int res = 0; for (int i = 3; i <= n - 3; i += 2) res = max(pre[i] + suf[i], res); printf("%d\n", res); return 0; }