codeforces #30E Tricky and Clever Password KMP+Manacher+二分

题目大意:给定一个字符串 S ,要求分成A+prefix+B+middle+C+suffix6段,满足:
|prefix|=|suffix|
|middle| 为奇数
prefix+middle+suffix 为回文串
middle 外所有段长度都可以为 0
要求最大化|prefix|+|middle|+|suffix|,输出一组方案( |prefix|=|suffix|=0 时只输出 middle )

首先我们发现 suffix 串是顶着右端点的,因此我们可以枚举 |suffix|
对于每个 |suffix| 我们需要求出最左侧能与 suffix 匹配的 prefix
我们发现随着 |suffix| 的增大, prefix 的位置是单调不减的,因此我们可以用KMP算法求出对于每个 |suffix| 最靠左的 prefix
现在对于一个确定的 prefix 串和 suffix 串,我们需要求出中间的区间内最长的回文子串 middle

现在就是区间最长回文子串的问题了

一种做法是这样的:
首先利用Manacher算法求出以每个点为中心的最长回文半径,然后对于每次询问我们二分答案:
设询问区间为 [L,R] ,二分长度为 M ,那么如果[L+M1,RM+1]区间最大的最长回文半径 M 则存在回文半径为 M 的回文子串,否则不存在
利用ST表即可做到单次询问O(logn)

我还YY出了一种SB做法:
观察这个题的所有询问,我们发现询问区间是依次包含的
因此我们可以从最小的区间开始依次拓展
每次拓展,假设当前拓展的是 R ,那么我们需要用以R结尾,长度不超过 RL+1 的最长回文子串来更新答案
这个用回文自动机+树上倍增就可以了

= =所以说是傻逼做法嘛

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 100100
#define MOD 999911657
#define BASE 2333
using namespace std;
int n,ans;
char s[M],rev_s[M];
int next[M],f[M],pos[M];
pair<int,int> intervals[M];
int log_2[M],a[M][17],_ans[M];
void KMP()
{
    int i,fix=0;
    for(i=2;i<=n;i++)
    {
        while( fix && rev_s[fix+1]!=rev_s[i] )
            fix=next[fix];
        if( rev_s[fix+1]==rev_s[i] )
            ++fix;
        next[i]=fix;
    }
    fix=0;
    for(i=1;i<=n;i++)
    {
        while( fix && rev_s[fix+1]!=s[i] )
            fix=next[fix];
        if( rev_s[fix+1]==s[i] )
            ++fix;
        if( i+fix>=n )
            return ;
        if(!pos[fix])
        {
            pos[fix]=i;
            intervals[fix]=make_pair(i+1,n-fix);
        }
    }
}
void Manacher()
{
    int i,id=1,mx=1;
    s[0]='$';
    for(i=1;i<=n;i++)
    {
        f[i]=min(mx-i+1,f[id+id-i]);
        while(s[i+f[i]]==s[i-f[i]])
            ++f[i];
        if(i+f[i]-1>mx)
            mx=i+f[i]-1,id=i;
    }
}
int Get_Max(int x,int y)
{
    int len=log_2[y-x+1];
    return max(a[x][len],a[y-(1<<len)+1][len]);
}
int Bisection(int x,int y)
{
    int l=1,r=y-x+2>>1;
    while(l+1<r)
    {
        int mid=l+r>>1;
        if( Get_Max(x+mid-1,y-mid+1)>=mid )
            l=mid;
        else
            r=mid;
    }
    return Get_Max(x+r-1,y-r+1)>=r?r:l;
}
int main()
{
    int i,j;
    scanf("%s",s+1);n=strlen(s+1);
    for(i=1;i<=n;i++)
        rev_s[i]=s[n-i+1];
    KMP();
    Manacher();
    pos[0]=1;intervals[0]=make_pair(1,n);
    for(i=2;i<=n;i++)
        log_2[i]=log_2[i>>1]+1;
    for(i=1;i<=n;i++)
        a[i][0]=f[i];
    for(j=1;j<=log_2[n];j++)
        for(i=1;i+(1<<j)-1<=n;i++)
            a[i][j]=max(a[i][j-1],a[i+(1<<j-1)][j-1]);
    for(i=0;pos[i];i++)
    {
        _ans[i]=2*Bisection(intervals[i].first,intervals[i].second)-1;
        if(i*2+_ans[i]>ans*2+_ans[ans])
            ans=i;
    }
    if(ans==0)
    {
        cout<<1<<endl;
        for(i=1;i<=n;i++)
            if(f[i]*2-1==_ans[0])
            {
                cout<<i-f[i]+1<<' '<<_ans[0]<<endl;
                return 0;
            }
    }
    else
    {
        cout<<3<<endl;
        cout<<pos[ans]-ans+1<<' '<<ans<<endl;
        int l=intervals[ans].first,r=intervals[ans].second;
        for(i=l;i<=r;i++)
            if( min(min(i-l,r-i)+1,f[i])*2-1==_ans[ans] )
            {
                cout<<i-(_ans[ans]>>1)<<' '<<_ans[ans]<<endl;
                break;
            }
        cout<<n-ans+1<<' '<<ans<<endl;
    }
    return 0;
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值