Description
顺序和逆序读起来完全一样的串叫做回文串。比如acbca是回文串,而abc不是(abc的顺序为“abc”,逆序为“cba”,不相同)。
输入长度为n的串S,求S的最长双回文子串T,即可将T分为两部分X,Y,(|X|,|Y|≥1)且X和Y都是回文串。
Input
一行由小写英文字母组成的字符串S。
Output
一行一个整数,表示最长双回文子串的长度。
Sample Input
baacaabbacabb
Sample Output
12
HINT
样例说明
从第二个字符开始的字符串aacaabbacabb可分为aacaa与bbacabb两部分,且两者都是回文串。
对于100%的数据,2≤|S|≤10^5
思路
这是个裸题,求两个连在一起的回文串的最长长度。
我们可以用回文树来求的
第 i 个字母之前的最长回文串的长度是多少,
然后倒这来就可以求得
第 i 个字母之后的最长回文串的长度是多少,
两个加起来就好了。
这个是回文树的一些主要数组。
Fail 失配指针。像AC自动机差不多的失配指针,这个指向的是同样回文串结尾的最长回文串。
len 当前回文串的长度。
s[] 一个个加入新的字母。
n 当前加入的是第几个字符。
p 当前是第几个节点。
num[i] 代表 i 这个节点所代表的回文串中有多少个本质不同的回文串。
cnt[i] 代表 i 这个节点所代表的回文串一共出现了多少次。 这个最后要 count() 一下。
强有力的博客
https://blog.csdn.net/u013368721/article/details/42100363
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+1000;
int Next[N][30],Fail[N],len[N];
int s[N],last,n,p,num[N],cnt[N],sum[N];
int s1[N],s2[N];
int newnode(int x){ //新加一个节点。
for (int i = 0; i < 30; i++)
Next[p][i] = 0; //加上 i 这个字母可以到达的后继节点。
cnt[p] = num[p] = 0;
len[p] = x;
return p++;
}
void init(){ // 初始化,首先要见两个点,偶数节点,和奇数节点。
p = 0;
newnode(0); newnode(-1);
last = 0, n = 0;
s[n] = -1; Fail[0] = 1;
return;
}
int getfail(int x){
while(s[n-len[x] - 1] != s[n]) x = Fail[x]; //找到满足的点。
return x;
}
int add(int c){
c -= 'a';
s[++n] = c;
int cur = getfail(last);
if (!Next[cur][c]){ //如果没有后继节点。新加入一个节点。
int now = newnode(len[cur] + 2);
Fail[now] = Next[getfail(Fail[cur])][c];
Next[cur][c] = now;
num[now] = num[Fail[now]] + 1; //
}
last = Next[cur][c];
cnt[last]++;
return len[last];
}
void count(){ // count() 最后计算 cnt[]
for (int i = p - 1; i >= 0; --i)
cnt[Fail[i]] += cnt[i];
}
int main(){
char t[N];
int ll;
scanf("%s",t);
ll = strlen(t);
int Max = 0;
init(); //正着来一遍。
for (int i = 0; i < ll; i++)
s1[i] = add(t[i]);
count();
init(); //反着来一遍。
for (int i = ll-1; i >= 0; i--)
s2[i] = add(t[i]);
for (int i = 0; i < ll; i++)
Max = max(Max,s1[i] + s2[i+1]);
printf("%d\n",Max);
return 0;
}