P4555 [国家集训队]最长双回文串

传送门

搞回文串很容易想到manacher

把每个回文串看成线段

那就是求两个相邻线段的最长总长度

设 l[ i ] 表示左端点为 i-1 时线段的最大长度,r[ i ] 表示右端点为 i+1 时线段的最大长度

(这里 i 在manacher处理后的字符串a上,线段的最大长度是指原字符串上的长度)

那么ans=max(ans, l[ i ] + r[ i ] ) (a[ i ] == ' # ')

当 i 刚好为长线段的端点时的 l[ i ] 和 r[ i ] 在manacher 时可以处理好

当 i 在长线段的内部时 l[ i ] 和 r[ i ] 可以递推出来

l [ i ] =max(l[ i ],l[ i-2 ] -2) (i-2是因为 i 在a上,l[ i-2 ] - 2 是因为要保持回文,左右两边都要去掉)

r [ i ] 也差不多处理

具体看代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=1e7+7;
char s[N],a[N];
int f[N],l[N],r[N],ans,len;
inline void read_s()
{
    char ch=getchar();
    while(ch<'a'||ch>'z') ch=getchar();
    while(ch>='a'&&ch<='z') s[++len]=ch,ch=getchar();
}
int main()
{
    read_s();
    len=len*2+1;
    for(int i=1;i<=len;i++)
        a[i]= i&1 ? '#' : s[i>>1];
    a[0]='$';
    int pos=0,mx=0;
    for(int i=1;i<=len;i++)
    {
        f[i]= i>=mx ? 1 : min(f[pos*2-i],mx-i);
        while(a[i+f[i]]==a[i-f[i]]) f[i]++;
        if(i+f[i]>mx) mx=i+f[i],pos=i;
        l[i-f[i]+1]=max(l[i-f[i]+1],f[i]-1);
        r[i+f[i]-1]=max(r[i+f[i]-1],f[i]-1);
    }
    for(int i=3;i<len;i+=2) l[i]=max(l[i],l[i-2]-2);//只要计算'#'的l,r数组
    for(int i=len-2;i>=3;i-=2) r[i]=max(r[i],r[i+2]-2);
    for(int i=3;i<len;i+=2) ans=max(ans,l[i]+r[i]);//主要i!=1&&i!=len,题目要求线段长度不能为0
    printf("%d",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/LLTYYC/p/9717299.html