【题目链接】
【思路要点】
- 一定存在一个最长的扭动回文串是从回文中心所在串的某一个极长回文子串开始,一侧向回文中心所在串匹配,另一侧扭向另一个串匹配。因为任意一个扭动的回文串都对应了一个同样长的满足上述要求的扭动的回文串。
- 枚举回文中心,问题转化为了两个字符串的LCP,字符串哈希和后缀结构均可解决。
- 时间复杂度\(O(NLogN)\)。
【代码】
#include<bits/stdc++.h> using namespace std; #define MAXN 400005 #define MAXLOG 20 int n, lenx[MAXN], leny[MAXN]; char x[MAXN], y[MAXN]; int homex[MAXN], homey[MAXN]; int Manacher(char *x, int *len) { static char s[MAXN]; for (int i = 1; i <= n; i++) { s[i * 2] = x[i]; s[i * 2 - 1] = '#'; } s[0] = '$'; s[n * 2 + 1] = '#'; s[n * 2 + 2] = '$'; int pos = 0, home = 0, ans = 0; for (int i = 1; i <= 2 * n + 1; i++) { if (i > pos) { len[i] = 1; while (s[i - len[i]] == s[i + len[i]]) len[i]++; pos = i + len[i] - 1; home = i; } else { int p = home * 2 - i; if (i + len[p] - 1 < pos) len[i] = len[p]; else { len[i] = pos - i + 1; while (s[i - len[i]] == s[i + len[i]]) len[i]++; pos = i + len[i] - 1; home = i; } } ans = max(ans, len[i] - 1); } return ans; } struct Suffix_Automaton { int child[MAXN][26]; int father[MAXN], depth[MAXN]; int parent[MAXN][MAXLOG], cnt[MAXN]; vector <int> a[MAXN]; int root, size, last; int new_node(int dep) { depth[size] = dep; father[size] = 0; a[size].clear(); memset(child[size], 0, sizeof(child[size])); memset(parent[size], 0, sizeof(parent[size])); return size++; } void init() { size = 0; root = new_node(0); } void Extend(int ch) { int np = child[last][ch]; if (np) { if (depth[last] + 1 == depth[np]) last = np; else { int nq = new_node(depth[last] + 1); father[nq] = father[np]; father[np] = nq; memcpy(child[nq], child[np], sizeof(child[np])); for (int p = last; child[p][ch] == np; p = father[p]) child[p][ch] = nq; last = nq; } } else { np = new_node(depth[last] + 1); int p = last; for (; child[p][ch] == 0; p = father[p]) child[p][ch] = np; if (child[p][ch] == np) last = np; else { int q = child[p][ch]; if (depth[q] == depth[p] + 1) { father[np] = q; last = np; } else { int nq = new_node(depth[p] + 1); father[nq] = father[q]; father[q] = father[np] = nq; memcpy(child[nq], child[q], sizeof(child[q])); for (; child[p][ch] == q; p = father[p]) child[p][ch] = nq; last = np; } } } } void insert(char *s, int *home) { int len = strlen(s + 1); last = root; for (int i = 1; i <= len; i++) { Extend(s[i] - 'A'); home[i] = last; } } void work(int pos, int depth) { cnt[pos] = depth; parent[pos][0] = father[pos]; for (int i = 1; i < MAXLOG; i++) parent[pos][i] = parent[parent[pos][i - 1]][i - 1]; for (unsigned i = 0; i < a[pos].size(); i++) work(a[pos][i], depth + 1); } void build() { for (int i = 1; i < size; i++) a[father[i]].push_back(i); work(root, 0); } int query(int x, int y) { if (cnt[x] < cnt[y]) swap(x, y); for (int i = MAXLOG - 1; i >= 0; i--) if (cnt[parent[x][i]] >= cnt[y]) x = parent[x][i]; if (x == y) return depth[x]; for (int i = MAXLOG - 1; i >= 0; i--) if (parent[x][i] != parent[y][i]) { x = parent[x][i]; y = parent[y][i]; } return depth[father[x]]; } } SAM; int main() { freopen("BZOJ4755.in", "r", stdin); freopen("BZOJ4755.out", "w", stdout); scanf("%d\n%s\n%s", &n, x + 1, y + 1); int ans = max(Manacher(x, lenx), Manacher(y, leny)); SAM.init(); SAM.insert(x, homex); for (int i = 1, j = n; i < j; i++, j--) swap(y[i], y[j]); SAM.insert(y, homey); for (int i = 1, j = n; i < j; i++, j--) swap(homey[i], homey[j]); SAM.build(); for (int i = 1; i <= n; i++) { int tmp = lenx[i * 2] / 2; if (tmp == i) continue; ans = max(ans, 2 * SAM.query(homex[i - tmp], homey[i + tmp - 1]) + tmp * 2 - 1); } for (int i = 1; i < n; i++) { int tmp = lenx[i * 2 + 1] / 2; if (tmp == i) continue; ans = max(ans, 2 * SAM.query(homex[i - tmp], homey[i + tmp]) + tmp * 2); } for (int i = 1; i <= n; i++) { int tmp = leny[i * 2] / 2; if (i + tmp > n) continue; ans = max(ans, 2 * SAM.query(homex[i - tmp + 1], homey[i + tmp]) + tmp * 2 - 1); } for (int i = 1; i < n; i++) { int tmp = leny[i * 2 + 1] / 2; if (i + tmp + 1 > n) continue; ans = max(ans, 2 * SAM.query(homex[i - tmp + 1], homey[i + tmp + 1]) + tmp * 2); } printf("%d\n", ans); return 0; }