【BZOJ 2565】最长双回文串
题意:
给出一个字符串,找到一个最长的双回文子串。
双回文子串:
即可以将一个子串\(T\)分成两部分\(X、Y\),并且\(|X|、|Y| \leq 1\),并且\(X\)和\(Y\)都是回文串。
思路:
考虑做两遍\(PAM\),求出以字符\(i\)结尾的最长回文串长度以及以字符\(i\)结尾的最长回文串长度。
那么枚举分界点更新答案即可。
下面的做法是错的:
做一遍\(PAM\),然后考虑\(f[i]\)表示以字符\(i\)结尾的最长回文子串,然后用\(f[i] + f[i - f[i]]\)去更新答案。
因为在最优答案中的结尾并不一定是最长的一段再加上前面衔接一段。
比方说:
aabaaaaaa
这个最大长度显然是整个串。
但是注意到用这种方法搞的最大长度显然是最后一个字符去更新的答案,但是以最后一个字符为结尾的最长长度是\(6\),这样前面的\(aab\)就不可以接起来了。
所以是错的。
代码:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define N 200010
#define ALP 26
struct PAM{
int Next[N][ALP];
int fail[N];
int cnt[N];
int num[N];
int len[N];
int s[N];
int last;
int n;
int p;
int newnode(int w){
for(int i=0;i<ALP;i++)
Next[p][i] = 0;
cnt[p] = 0;
num[p] = 0;
len[p] = w;
return p++;
}
void init(){
p = 0;
newnode(0);
newnode(-1);
last = 0;
n = 0;
s[n] = -1;
fail[0] = 1;
}
int get_fail(int x){
while(s[n-len[x]-1] != s[n]) x = fail[x];
return x;
}
void add(int c){
c -= 'a';
s[++n] = c;
int cur = get_fail(last);
if(!Next[cur][c]){
int now = newnode(len[cur]+2);
fail[now] = Next[get_fail(fail[cur])][c];
Next[cur][c] = now;
num[now] = num[fail[now]] + 1;
}
last = Next[cur][c];
cnt[last]++;
}
void count(){
for(int i=p-1;i>=0;i--)
cnt[fail[i]] += cnt[i];
}
}pam;
char s[N];
int l[N], r[N];
int main() {
while (scanf("%s", s + 1) != EOF) {
pam.init();
int len = strlen(s + 1);
int res = 0;
for (int i = 1; i <= len; ++i) {
pam.add(s[i]);
l[i] = pam.len[pam.last];
}
pam.init();
for (int i = len; i >= 1; --i) {
pam.add(s[i]);
r[i] = pam.len[pam.last];
}
for (int i = 1; i < len; ++i) res = max(res, l[i] + r[i + 1]);
printf("%d\n", res);
}
return 0;
}

浙公网安备 33010602011771号