/*
TASK:calfflac
LANG:C++
*/
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int max_n = 200000;
char origin_string[max_n];//原串
int reflect[max_n * 2];//子串的第i个字母,在原串的位置
int origin_string_length(0), son_string_length(0);//原串长度,子串长度
char son_string[max_n * 2];//子串
int son_string_extend_length[max_n * 2];//子串第i个位置,可以左右扩展的最大长度
bool IF_LETTER(char &T)
{
if ('a' <= T && T <= 'z') return true;
if ('A' <= T && T <= 'Z') return true;
return false;
}
bool IF_SAME(char A, char B)
{
if ('a' <= A && A <= 'z') A += 'A' - 'a';
if ('a' <= B && B <= 'z') B += 'A' - 'a';
if (A == B) return true;
return false;
}
void manacher()
{
int mid = 0, max_length = 0; //起点位置 最长扩展长度
for (int i = 1; i != son_string_length - 1; ++ i)
{
int len = 0;//从i个地方能扩展的最大长度,默认为0
if (i < mid + max_length) len = min(son_string_extend_length[mid - (i - mid)], mid + max_length - i);
//如果已经在最长的回文内, 那么就是回文串长度边界的位置, 或者就是之前求过的值
while (IF_SAME(son_string[i + len + 1], son_string[i - len - 1])) ++ len;
//更新回文
if (i + len > mid + max_length)
{
mid = i;
max_length = len;
//更新回文串最长位置
}
son_string_extend_length[i] = len;//记录
}
//下面就是处理答案,因为开头是-开头,所以如果回文串涉及开头的话,那么会导致串减少。所以把所有以#结尾的串的扩展长度-1
for (int i = 1; i != son_string_length - 1; ++ i) if (son_string[i + son_string_extend_length[i]] == '#') -- son_string_extend_length[i];
int ans = 0, P;
for (int i = 1; i != son_string_length - 1; ++ i)
if (son_string_extend_length[i] > ans)
{
ans = son_string_extend_length[i];
P = i;
}
int begin = reflect[P - ans];
int end = reflect[P + ans];
cout<<ans + 1<<endl;
for (int i = begin; i != end + 1; ++ i) printf("%c",origin_string[i]);
cout<<endl;
}
int main()
{
freopen("calfflac.in", "r", stdin);
freopen("calfflac.out", "w", stdout);
son_string[son_string_length++] = '+';
char ch;
while ((ch = getchar()) != EOF)
{
origin_string[origin_string_length++] = ch;
if (IF_LETTER(ch))
{
son_string[son_string_length++] = ch;
reflect[son_string_length - 1] = origin_string_length - 1;
son_string[son_string_length++] = '#';
}
}
son_string[son_string_length] = '-';
manacher();
return 0;
}
manacher算法,可以在O(n)的时间内解决最长回文子串问题。
这个题显然是最长回文子串问题,只不过要把里面的串抽出来,但是归根结底,还是最长回文子串问题。
下面介绍manacher算法:
在求最长回文串的时候,我们做了很多无用运算,如果能有效利用这些运算,就可以大大的降低时间复杂度。
举一个例子
0123456789ABCDEFG //这样表示第串第i个位置, 第一个就是0号位置,第10个就是A位置……
aba??+??aba //这就是串
↑ //b的最长子串就是3。 从1位置开始,可以向左或者向右扩展1个单位长度
aba??+??aba
↑ //+这个位置,也就是5位置,可以向左向右扩展5个长度。
aba??+??aba
↑ //假如我们要求这个b字母,也就是9位置,以他为中心的回文子串长度?
重点来了:
因为是回文!9位置,包括在以5位置为中心,向右扩展5个长度的回文串之中! 这么说的话,可以说明89A位置,和210位置的串是一模一样的!
也就是说,我们可以直接用1位置已经得出的以1为中心的回文串的答案,直接带进9位置的答案中!
那么,只要考虑一下边界情况。
1、
??aba??+??aba??+ 这个串的情况,
以+为中心的回文串不包括最后的+,这个情况还需要继续拓展
2、aabaa+aabcc 这个情况不想多说……
3、为了不考虑奇数偶数串的问题,把所有串改为
+a#b#c#d#e#f#g- 这样的串来处理
就不用考虑那么多了。
同时,这样的串,除了包含头尾的串,所有的回文串都是以#开头,以#结尾。 在输出最常串的时候,如果答案在头尾,是要考虑去掉多余的#问题的。
我当时考虑时间复杂计算也是醉了~后来仔细想想就是O(n)的拉~~~
k,为以某个点为中心,向右扩展的最大长度。 那么如果以i为中心,i向右可以扩展p个长度,i+p<k,那么显然直接得到结果
如果i+p>k,那么就正好从k开始扩展,同时更新了k。 也就是说,时间复杂度是O(n)的