Codeforces-1326D Prefix-Suffix Palindrome
题面
This is the hard version of the problem. The difference is the constraint on the sum of lengths of strings and the number of test cases. You can make hacks only if you solve all versions of this task.
You are given a string \(s\), consisting of lowercase English letters. Find the longest string, \(t\), which satisfies the following conditions:
- The length of \(t\) does not exceed the length of \(s\).
- \(t\) is a palindrome.
- There exists two strings \(a\) and \(b\) (possibly empty), such that \(t = a + b\) ( "\(+\)" represents concatenation), and \(a\) is prefix of \(s\) while \(b\) is suffix of \(s\).
Input
The input consists of multiple test cases. The first line contains a single integer \(t\) (\(1 \leq t \leq 10^5\)), the number of test cases. The next \(t\) lines each describe a test case.
Each test case is a non-empty string \(s\), consisting of lowercase English letters.
It is guaranteed that the sum of lengths of strings over all test cases does not exceed \(10^6\).
Output
For each test case, print the longest string which satisfies the conditions described above. If there exists multiple possible solutions, print any of them.
Example
input
5
a
abcdfdcecba
abbaxyzyx
codeforces
acbba
output
a
abcdfdcba
xyzyx
c
abba
Note
In the first test, the string $s = $"a" satisfies all conditions.
In the second test, the string "abcdfdcba" satisfies all conditions, because:
- Its length is \(9\), which does not exceed the length of the string \(s\), which equals \(11\).
- It is a palindrome.
- "abcdfdcba" \(=\) "abcdfdc" \(+\) "ba", and "abcdfdc" is a prefix of \(s\) while "ba" is a suffix of \(s\).
It can be proven that there does not exist a longer string which satisfies the conditions.
In the fourth test, the string "c" is correct, because "c" \(=\) "c" \(+\) "" and \(a\) or \(b\) can be empty. The other possible solution for this test is "s".
题意
给定一个字符串,求出最长的,由该字符串前缀和后缀组成的回文字符串
题解
直接马拉车算法,可以很简单的想到最长的回文串是由\(s+"一段回文串"+s.reverse()\)组成的,所以我们先分别从串的开头和结尾找到相等的字符串最长可以延伸的长度,然后枚举每个点的最长回文串,看看能不能拓展即可.
这题思路很简单,主要是想贴一下自己wa了无数发的代码
#include <bits/stdc++.h>
using namespace std;
const int N = 2e6 + 10;
char s[N];
char ss[N * 2];
int p[N * 2];
int init() {//形成新的字符串
int len = strlen(s);//len是输入字符串的长度
ss[0] = '$';
ss[1] = '#';
int j = 2;
for(int i = 0; i < len; i++) {
ss[j++] = s[i];
ss[j++] = '#';
}
ss[j] = '^';
ss[j + 1] = '\0';
return j;// 返回长度
}
int f[N], g[N];
void Manacher() {
int len = init();//取得新字符串的长度, 完成向s_new的转换
for (int i = 1; i <= len; i++) p[i] = 0;
int c = 0, r = 0;//c: 回文串中心 r: 右边界
for(int i = 1; i <= len; i++) {
int x = 2 * c - i;//关于c对称的坐标
if (r > i) {
p[i] = min(r - i, p[x]);//如果i处于一个回文串中,那么他的长度等于min(关于回文串中心对称的那个点的p,右边界到这个点的串长度)
}
else p[i] = 0;
while (ss[i + 1 + p[i]] == ss[i - 1 - p[i]]) p[i]++;//进行拓展,由于用之前的回文串更新了一次p,从而复杂度不会爆炸
if (i + p[i] > r) {//更新右边界和中心
c = i;
r = i + p[i];
}
}
for (int i = 1; i <= len; i++) {
int now = p[i];
int st = (i - now) / 2;//回文串的起点
f[st] = max(f[st], p[i]);
if (st + now - 1 >= 0) g[st + now - 1] = max(g[st + now - 1], p[i]);
}
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
scanf("%s", s);
int n = strlen(s);
for (int i = 0; i <= n; i++) f[i] = 0, g[i] = 0;
Manacher();
g[0] = 1;
int mx = f[0], ansl = f[0] - 1, ansr = n;
if (g[n - 1] > mx) {
mx = g[n - 1];
ansl = -1;
ansr = n - g[n - 1];
}
int l = 0, r = n - 1;
int cnt = 1;
while (s[l] == s[r] && l < r) {
if (2 * cnt > mx) {
mx = 2 * cnt;
ansl = l;
ansr = r;
}
if (f[l + 1] + 2 * cnt > mx && f[l + 1] + 2 * cnt <= n) {
mx = f[l + 1] + 2 * cnt;
ansl = l + f[l + 1];
ansr = r;
}
if (g[r - 1] + 2 * cnt > mx && g[r - 1] + 2 * cnt <= n) {
mx = g[r - 1] + 2 * cnt;
ansl = l;
ansr = r - g[r - 1];
}
l++, r--, cnt++;
}
for (int i = 0; i <= ansl; i++) putchar(s[i]);
for (int i = ansr; i < n; i++) putchar(s[i]);
puts("");
}
return 0;
}
这里我用f[i]表示了i开始的最长的回文串,g[i]表示了i结束的最长的回文串,然后枚举开头结尾看看能不能拓展一个回文串的长度,同时保证长度相加不大于n,但这又出现了一个问题,我的f和g表示的最长的回文串,但是有可能是加上一个最长的回文串导致长度超过了n而不能拓展,从而长度变短,然后不加最长的,加较短的回文串反而因为没有超过n,能够成为答案.
我用了些奇奇怪怪的方法找到了cf中wa的这个样例(什么时候cf提供一下下载数据?)
wkaabaaaakw
就是上面这个字符串,我的程序输出为wkaabaakw,其实答案应该是wkaaaaaakw,这里没有找到这个答案就是因为在枚举到g[6]时g[6]=5(aabaa),从而没有找到wkaa + "aa" + aakw的答案
所以要先枚举所有回文串,直接在马拉车之后的p数组枚举,计算答案.
以下为ac代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
char s[N];
char ss[N * 2];
int p[N * 2];
int main() {
int t;
scanf("%d", &t);
while (t--) {
scanf("%s", s);
int n = strlen(s);
//manacher
ss[0] = '$', ss[1] = '#';
int len = 1;
for (int i = 0; i < n; i++) ss[++len] = s[i], ss[++len] = '#';
ss[++len] = '^';
for (int i = 1; i < len; i++) p[i] = 0;
int c = 0, r = 0;//c: 回文串中心 r: 右边界
for(int i = 1; i < len; i++) {
int x = 2 * c - i;//关于c对称的坐标
if (r > i) {
p[i] = min(r - i, p[x]);//如果i处于一个回文串中,那么他的长度等于min(关于回文串中心对称的那个点的p,右边界到这个点的串长度)
}
else p[i] = 0;
while (ss[i + p[i] + 1] == ss[i - p[i] - 1]) ++p[i];//进行拓展,由于用之前的回文串更新了一次p,从而复杂度不会爆炸
if (i + p[i] > r) {//更新右边界和中心
c = i;
r = i + p[i];
}
}
int d = 0;
while (s[d] == s[n - d - 1] && d < n) d++;
int mx = 0, ansl, ansr, m;
for (int i = 1; i < len; i++) {
int st = (i - p[i]) / 2, ed = (i + p[i]) / 2 - 1;
int x = min(st - 1, n - ed - 1);
if (x <= d && 2 * x + p[i] > mx) {
mx = 2 * x + p[i];
m = x;
ansl = st;
ansr = ed;
}
}
for (int i = 0; i <= m; i++) putchar(s[i]);
for (int i = ansl; i <= ansr; i++) putchar(s[i]);
for (int i = n - m; i < n; i++) putchar(s[i]);
puts("");
}
return 0;
}
(什么时候开学啊,在家网速太慢不敢在线cf)