bzoj 2803: [Poi2012]Prefixuffix manachar

题意

对于两个串S1、S2,如果能够将S1的一个后缀移动到开头后变成S2,就称S1和S2循环相同。例如串ababba和串abbaab是循环相同的。
给出一个长度为n的串S,求满足下面条件的最大的L:
1. L<=n/2
2. S的L前缀和S的L后缀是循环相同的。
n<=1000000 n <= 1000000

分析

网上的题解貌似都是推了些性质出来,但如果像我一样菜推不动性质的话还有一种比较易懂的做法。
不难发现就是要求一个最大的L满足存在一个数x,S[1,x]=S[n-x+1,n],且S[x+1,L]=S[n-L+1,n-x]。
假设只要满足S[1,x]=S[n-x+1,n]的话要怎么做呢?
我们可以把字符串进行变换,变成这个样子:S[1] S[n] S[2] S[n-1] S[3] S[n-2]…
设变换后的字符串为T,那么若有S[1,x]=S[n-x+1,n],则必有T[1,x*2]是一个回文串。
那么问题就转变成了求从T的起点开始的最长的双回文串。
直接manachar即可。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define MAX(x,y) x=max(x,y)
using namespace std;

const int N=1000005;

int n,s[N*2],len[N*2],f[N];
char str[N],a[N];

void manachar()
{
    for (int i=1;i<=n*2+1;i+=2) s[i]=30;
    for (int i=1;i<=n;i++) s[i*2]=a[i]-'a'+1;
    int mx=0,pos=0;
    for (int i=1;i<=n*2+1;i++)
    {
        if (mx>=i) len[i]=min(mx-i+1,len[pos*2-i]);
        else len[i]=1;
        while (i-len[i]>0&&i+len[i]<=n*2+1&&s[i-len[i]]==s[i+len[i]]) len[i]++;
        if (i+len[i]-1>mx) mx=i+len[i]-1,pos=i;
        if (s[i]==30) MAX(f[(i-len[i]+2)/2],len[i]/2*2);
    }
}

int main()
{
    scanf("%d%s",&n,str+1);
    for (int i=1;i<=n/2;i++) a[i*2-1]=str[i];
    for (int i=2,j=0;i<=n;i+=2,j++) a[i]=str[n-j];
    n-=n&1;
    manachar();
    int ans=0;
    for (int i=1;i<=n;i++)
    {
        MAX(f[i+1],f[i]-2);
        if (i&1) continue;
        if (len[i+1]/2*2==i) ans=max(ans,i+f[i+1]);
    }
    printf("%d",ans/2);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值