Prefix-Suffix Palindrome (Hard version)
题意:
- 给定一个串 s s s,找出一个回文串 t t t,使得 t t t = s . p r e f i x s.prefix s.prefix + s . s u f f i x s.suffix s.suffix,并且 t t t的长度不大于 s s s.
思路:
- 我们找到最大的 p p p,使得 s [ 0 ] = s [ l e n − 1 ] , s [ 1 ] = s [ l e n − 2 ] , . . . , s [ p ] = s [ l e n − 1 − p ] s[0]=s[len-1], s[1]=s[len-2], ..., s[p]=s[len-1-p] s[0]=s[len−1],s[1]=s[len−2],...,s[p]=s[len−1−p]
- 串 s s s去掉第一步找到的 s [ 0 , p ] s[0, p] s[0,p]和 s [ l e n − 1 − p , l e n − 1 ] s[len-1-p, len-1] s[len−1−p,len−1],记剩下的中间的子串为 s u b sub sub. 我们接下来的任务是找串 s u b sub sub的最长前缀回文,或者最长后缀回文。
- 我们知道KMP的前缀数组: n e x t [ i ] next[ i ] next[i]表示 s u b [ 0 , i − 1 ] sub[0, i-1] sub[0,i−1]最大相同前后缀的长度。所以利用这一点我们可以求 s u b + ∗ + s u b ′ sub+*+sub' sub+∗+sub′的 n e x t next next数组, n e x t [ l e n ] next[len] next[len]即为最长前缀回文的长度。
- 至于为什么中间要加一个 ∗ * ∗,是因为 n e x t [ ] next[ \ ] next[ ]数组是前后缀可以交叉的最大相同前后缀的长度,如果中间不隔开,那么可能会造成得到的最长前缀回文是非法的。
样例:uwwuw
#include <bits/stdc++.h>
using namespace std;
const int maxN = 2000100;
int Next[maxN];
void GetNext(string t, int len)
{
int i = 0, j = -1;
Next[0] = -1;
while(i < len)
{
if(j == -1 || t[i] == t[j])
{
++i; ++ j;
if(t[i] != t[j])
Next[i] = j;
else
Next[i] = Next[j];
} else j = Next[j];
}
}
int main()
{
int t;
while(cin >> t)
{
while(t -- )
{
string str, L, R, ans1, ans2;
cin >> str;
int len = str.length();
for(int i = 0; i < len / 2; ++ i )
{
if(str[i] == str[len - 1 - i])
R += str[i];
else
break;
}
L = R;
reverse(R.begin(), R.end());
int lenLR = R.length(), lenMid = len - lenLR * 2;
string _sub = str.substr(lenLR, lenMid), sub = _sub;
reverse(_sub.begin(), _sub.end());
lenMid = lenMid << 1 | 1;
GetNext(sub + '#' + _sub, lenMid);
ans1 = L + sub.substr(0, Next[lenMid]) + R;
GetNext(_sub + '#' + sub, lenMid);
ans2 = L + _sub.substr(0, Next[lenMid]) + R;
if(ans1.length() > ans2.length())
cout << ans1 << endl;
else
cout << ans2 << endl;
}
}
return 0;
}