Description
据说如果你给无限只母牛和无限台巨型便携式电脑(有非常大的键盘),那么母牛们会制造出世上最棒的回文。你的工作就是去寻找这些牛制造的奇观(最棒的回文)。 在寻找回文时不用理睬那些标点符号、空格(但应该保留下来以便做为答案输出),只用考虑字母’A’-‘Z’和’a’-‘z’。要你寻找的最长的回文的文章是一个不超过20,000个字符的字符串。 我们将保证最长的回文不会超过2,000个字符(在除去标点符号、空格之前)。
Input
输入文件不会超过20,000字符。这个文件可能一行或多行,但是每行都不超过80个字符(不包括最后的换行符)。
Output
输出的第一行应该包括找到的最长的回文的长度。 下一行或几行应该包括这个回文的原文(没有除去标点符号、空格),把这个回文输出到一行或多行(如果回文中包括换行符)。 如果有多个回文长度都等于最大值,输出最前面出现的那一个。
Sample Input
Confucius say: Madam, I’m Adam.
Sample Output
11
Madam, I’m Adam
思路:
该题最核心的问题应该就是最长回文子串了吧,再在这基础上做一些变形。
最长回文子串要用Manacher算法(感谢Manacher)。可是Manacher算法要的是有效字符组成的字符串,所以,在用Manacher算法之前,要对原串进行一下处理,处理后得到处理串t,同时还要用一个p数组记录字符串t中每个字符在原串的位置,(这个有什么用了?之后会用到的)。Manacher算法会得到最长回文字子串的中心以及半径,这样我们可以求出最长回文字子串在串t中的起点和终点。此时把起点和终点当作p数组的下标就可以得到最长回文子串在原串中的起点和终点,这样得到的起点和终点构成的子字符串就是题目所要求的结果了。
这个题的原串有多行,要用getchar 搭配EOF来处理,一开始特懵逼。
#include <iostream>
#include <cstdio>
#include <vector>
#define N 20004
using namespace std;
int resLen, resCenter;
void Manacher(string s)
{
string t;
t = "$#";
for(int i = 0; i < s.size(); i++)
{
t += s[i];
t += '#';
}
vector<int>p(t.size(), 0);
int id = 0, mx = 0;
for(int i = 1; i < t.size(); i++)
{
p[i] = mx>i ? min(p[2 * id - i], mx - i) : 1;
while(t[i + p[i]] == t[i - p[i]])
p[i]++;
if( mx < i + p[i] )
{
id = i;
mx = i + p[i];
}
if(resLen < p[i])
{
resLen = p[i];
resCenter = i;
}
}
}
int main()
{
int cnt=0;
char c,a[N]= {0};
string s;
while((c=getchar())!=EOF)
a[cnt++]=c;
int p[N]= {0},g=0;
for(int i = 0; i < cnt; i++)
{
if( a[i] >= 'A' && a[i] <= 'Z' )
{
p[g++]=i;
s.push_back(a[i]+32);
}
else if(a[i] >= 'a' && a[i] <= 'z')
{
p[g++]=i;
s.push_back(a[i]);
}
}
Manacher(s);
int sta = (resCenter - resLen) / 2, len = resLen - 1;
printf("%d\n",len);
for(int i = p[sta]; i <= p[sta + len - 1]; i++)
printf("%c",a[i]);
printf("\n");
return 0;
}