码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2015-05-13 22:04:47      阅读:259      评论:0      收藏:0      [点我收藏+]

标签:codeforces   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+M?1,R?M+1]区间最大的最长回文半径M则存在回文半径为M的回文子串,否则不存在
利用ST表即可做到单次询问O(logn)

我还YY出了一种SB做法:
观察这个题的所有询问,我们发现询问区间是依次包含的
因此我们可以从最小的区间开始依次拓展
每次拓展,假设当前拓展的是R,那么我们需要用以R结尾,长度不超过R?L+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;
}

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

标签:codeforces   kmp算法   manacher算法   二分   

原文地址:http://blog.csdn.net/popoqqq/article/details/45696305

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!