Codeforces 1326D2 - Prefix-Suffix Palindrome (Hard version) (Manacher算法)
思路
先求出最长的回文的前后缀,然后求每个最长回文区间能不能和前缀或后缀接起来,取接起来后长度最大的那个。
基本就是纯的马拉车算法了。就是要注意边界处理的细节,很容易出问题。我把前后缀的位置处理为开区间,回文区间为闭区间。这样边界相等就可以接起来。详见代码。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e6 + 10;
char ss[N];
string s;
string ans;
int len[N];
void mana() { //马拉车
s.clear();
ans.clear();
s.push_back('$');
s.push_back('#');
for(int i = 0; ss[i]; i++) {
s.push_back(ss[i]);
s.push_back('#');
}
int a = 1, p = 1;
for(int i = 1; i < s.size(); i++) {
if(i < p && len[2 * a - i] < p - i) len[i] = len[2 * a - i];
else {
int l = p - i;
while(i + l < s.size() && s[i + l] == s[i - l]) l++;
len[i] = l;
a = i;
p = i + l;
}
}
//for(int i = 0; ss[i]; i++) cout << len[(i + 1) * 2] / 2 << " ";
}
int main() {
ios::sync_with_stdio(false);
int t;
cin >> t;
while(t--) {
cin >> ss;
mana();
int p1 = 1, p2 = s.size() - 1;
while(p1 < p2 && s[p1] == s[p2]) p1++, p2--;
if(p1 >= p2) {cout << ss << endl; continue;}
int mx = 0, mxp, fx;
for(int i = 1; i < s.size(); i++) {
int l = i - len[i] + 1;
int r = i + len[i] - 1; //l, r为回文区间(闭区间)
if(l > p1 && r < p2) continue; //[l, r]与前后缀都接不起来
if(l - 1 < s.size() - r - 1) { //[l, r]离前后边界哪个就接到哪个前或后缀
if(2 * (i - 1) + 1 > mx) {
mxp = i;
mx = 2 * (i - 1) + 1;
fx = 0;
}
} else {
if((s.size() - i - 1) * 2 + 1 > mx) {
mxp = i;
mx = (s.size() - i - 1) * 2 + 1;
fx = 1;
}
}
}
if(fx == 0) { //接到不同前后缀方向不同
int d = (mx - 1) / 2;
for(int i = mxp - d; i < mxp; i++) ans.push_back(s[i]);
for(int i = mxp; i >= mxp - d; i--) ans.push_back(s[i]);
for(char ch : ans) if(ch != '#') cout << ch;
cout << endl;
} else {
int d = (mx - 1) / 2;
for(int i = mxp + d; i > mxp; i--) ans.push_back(s[i]);
for(int i = mxp; i <= mxp + d; i++) ans.push_back(s[i]);
for(char ch : ans) if(ch != '#') cout << ch;
cout << endl;
}
}
}