题目大意:
就是现在有一个长度为奇数的个回文串串T被加密成了 A + prefix + B + middle + C + suffix的形式, 其中 T = prefix + middle + suffix , 这六个部分可以是空的, 现在对于给出的加密后的串求原串T的最大可能长度, 其中middle长度也是奇数, preifx长度和suffix长度相等, 输入的字符串长度<= 10^5
大致思路:
首先很容易观察到middle的中心就是T的中心, middle也是回文串, 先Manacher处理出每个字符为中心的回文半径, 那么枚举T的正中心将[i - R[i] + 1, i + R[i] - 1]作为middle一定是以i为中心下的最优解, 然后考虑最大化suffix和prefix, 用KMP预处理出以 j 位置字符为结尾的能和suffix匹配最大长度的字符串的长度, 即 match[j] 表示 T[j - k + 1, ... j - 1, j] == T'[0...k - 1] 的最大的k, T‘表示T的反向字符串, 那么预处理前缀和dp[i] = max(dp[i - 1], match[i])来表示区间[1, i]结尾的字符串中能匹配的最大长度, 于是剩下的就很明了了= =
这个思想是参见2014年国家集训队徐毅的论文上的, 可以去参考一下那篇论文
代码如下:
Result : Accepted Memory : 2164 KB Time : 62 ms
/*
* Author: Gatevin
* Created Time: 2015/3/29 9:26:17
* File Name: Chitoge_Kirisaki.cpp
*/
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;
#define maxn 100010
char s[maxn];
int R[maxn];
char rev[maxn];
int next[maxn];
int match[maxn];
int dp[maxn];
int tail[maxn];
void Manacher(char *s, int *R, int n)
{
int mx = 0, p = 0;
R[0] = 1;
for(int i = 1; i <= n; i++)
{
if(mx > i) R[i] = min(R[2*p - i], mx - i);
else R[i] = 1;
while(s[i - R[i]] == s[i + R[i]])
R[i]++;
if(i + R[i] > mx)
mx = i + R[i], p = i;
}
return;
}
void KMP(char *s, char *rev, int n)
{
memset(next, 0, sizeof(next));
for(int i = 1; i < n; i++)
{
int j = i;
while(j > 0)
{
j = next[j];
if(rev[i] == rev[j])
{
next[i + 1] = j + 1;
break;
}
}
}
memset(match, 0, sizeof(match));
for(int i = 1, j = 0; i < n + 1; i++)
if(s[i] == rev[j])
j++, match[i] = j;
else
while(j > 0)
{
j = next[j];
if(s[i] == rev[j])
{
j++;
match[i] = j;
break;
}
}
return;
}
vector <pair<int, int> > pos;
int main()
{
scanf("%s", s + 1);
int n = strlen(s + 1);
s[0] = '@', s[n + 1] = '$';
Manacher(s, R, n + 1);
for(int i = 0; i < n; i++)
rev[i] = s[n - i];
rev[n] = '\0';
KMP(s, rev, n);
int maxl = 0;
dp[1] = match[1];
tail[1] = 1;
for(int i = 1; i <= n; i++)
{
if(dp[i - 1] < match[i])
dp[i] = match[i], tail[i] = i;
else dp[i] = dp[i - 1], tail[i] = tail[i - 1];//tail[i]记录dp[i]对应最优解的串的末尾
}
for(int i = 1; i <= n; i++)//枚举middle中心
{
int middle = 2*R[i] - 1;
int pre_suf = min(dp[i - R[i]], n - (i + R[i] - 1));//prefix或suffix长度
if(middle + 2*pre_suf > maxl)
{
pos.clear();
maxl = middle + 2*pre_suf;
if(pre_suf > 0)
pos.push_back(make_pair(tail[i - R[i]] - pre_suf + 1, pre_suf));
pos.push_back(make_pair(i - R[i] + 1, 2*R[i] - 1));
if(pre_suf > 0)
pos.push_back(make_pair(n - pre_suf + 1, pre_suf));
}
}
printf("%d\n", (int)pos.size());
for(unsigned int i = 0; i < pos.size(); i++)
printf("%d %d\n", pos[i].first, pos[i].second);
return 0;
}