Problem
求每一种旋转串的最长回文子串,串长小于等于$10^6$
Solution
倍长字符串,转化为区间回文子串,$manacher$预处理倍长后的回文串
对于查询区间$[L,R]$最长回文子串
我们可以二分答案,检验区间$[L+x-1,R-x+1]$中是否存在长度大于$x$的回文中心即可
$ST$表预处理$r$数组的区间最大值,复杂度$O(nlogn)$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| #include <bits/stdc++.h> using namespace std; const int N = 1000000 + 10; int n, f[N << 1], r[N << 1]; char s[N], c[N << 1]; int dp[N << 1][21], lg2[N << 1]; void Init(int n) { for (int i = 2; i <= n; i++) lg2[i] = lg2[i / 2] + 1; for (int i = 1; i <= n; i++) dp[i][0] = r[i]; for (int j = 1; (1 << j) <= n; j++) for (int i = 1; i + (1 << j) - 1 <= n; i++) dp[i][j] = max(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]); } int Max(int l, int r) { if (l > r) swap(l, r); int k = lg2[r - l + 1]; return max(dp[l][k], dp[r - (1 << k) + 1][k]); } void manacher() { for (int i = 1; i <= n; i++) c[i << 1] = s[i], c[(i << 1) + 1] = '#'; c[1] = '#'; c[n << 1 | 1] = '#'; c[0] = '&'; c[(n + 1) << 1] = '$'; int j = 0, k; n = n << 1 | 1; for (int i = 1; i <= n;) { while (c[i - j - 1] == c[i + j + 1]) j++; r[i] = j; for (k = 1; k <= j && r[i] - k != r[i - k]; k++) r[i + k] = min(r[i - k], r[i] - k); i += k; j = max(j - k, 0); } } int main() { scanf("%d%s", &n, s + 1); int m = n; n = strlen(s + 1); for (int i = n + 1; i <= 2 * n; i++) s[i] = s[i - n]; n = n * 2; manacher(); Init(n); for (int i = 1; i <= m; i++) { int l = 1, r = m, ans = 1; int L = 2 * i, R = 2 * (i + m - 1); while (l <= r) { int mid = l + r >> 1; if (Max(L + mid - 1, R - mid + 1) >= mid) ans = mid, l = mid + 1; else r = mid - 1; } printf("%d\n", ans); } return 0; }
|