[BZOJ 2342] 双倍回文

Link:https://www.lydsy.com/JudgeOnline/problem.php?id=2342

 

Algorithm:

解决回文串问题,一般从对称轴下手

 

肯定先跑一边Manacher,(可以只记录长度为偶数的回文串)

枚举x为“主”对称轴,实际上对称轴在x到x+1之间。这样外层大的回文串wwRwwR就确定了。

接下来就只要枚举一半的回文串wwR的对称轴y了,从而用len(x+1,y)*4更新答案

当且仅当 y-m[y]<=x 并 y<=x+m[x]/2时是符合要求的x和y(由于回文串的性质,大于时不影响答案)

 

同时维护两个条件求最优解时,

我们先构造一个条件的单调性,再每次在排好序的队列中O(logN)地查询最优地符合第二个条件的数

 

于是我们先将序列按k-m[k]排序,将其放入set中,保证set中的k-m[k]<=当前的i

再每次在set中查询最大的k使得k<=i+m[i]/2即可

 

Code:

#include <bits/stdc++.h>

using namespace std;

const int MAXN=5e5+10;
char dat[MAXN];
int n,m[MAXN],pre[MAXN],res=0;

set<int> s;

void manacher()
{
    int mx=0,mid;
    for(int i=1;i<=n;i++)
    {
        if(i<mx) m[i]=min(mx-i,m[mid*2-i]);
        else m[i]=0;
        
        while(dat[i+m[i]+1]==dat[i-m[i]]) m[i]++;  //只查询长度为偶数的回文串
        
        if(i+m[i]>mx) mid=i,mx=m[i]+i;
    }
}

bool cmp(int x,int y)
{
    return x-m[x]<y-m[y];
}

int main()
{
    scanf("%d",&n);
    scanf("%s",dat+1);dat[0]='#';
    manacher();
    
    for(int i=1;i<=n;i++) pre[i]=i;
    sort(pre+1,pre+n+1,cmp);
    
    int cur=1;
    for(int i=1;i<=n;i++)
    {
        while(pre[cur]-m[pre[cur]]<=i && cur<=n)  //维护第一个条件的单调性
            s.insert(pre[cur]),cur++;
        set<int>::iterator it=s.upper_bound(i+m[i]/2);
        if(it!=s.begin())  //边界判断
            res=max(res,(*--it-i)*4);
    }
    cout << res;
    return 0;
}

 

Review:

1、解决回文串问题,一般从对称轴下手

根据条件不同,对枚举出的对称轴做不同的处理

 

2、灵活使用回文串的对称性,

大部分时候只用处理一半的字符,剩余的会由对称性保证正确性

 

3、同时维护两个条件求最优解时,

我们先构造一个条件的单调性(通过特殊条件排序实现)

posted @ 2018-05-23 09:20  NewErA  阅读(172)  评论(0编辑  收藏  举报