uva1470 - Casting Spells

链接

https://vjudge.net/problem/UVA-1470

题解

w w r w w r ww^rww^r wwrwwr这东西看起来十分回文
我想了半天,发现可以这样判断一个子串是否符合条件:
假设 w w w的长度为 u u u
那么第一个 w w w和第一个 w r w^r wr之间的位置肯定是一个对称轴,两边长度为 u u u的区域是关于这个轴对称的,也就是说这里有一个长度不低于 u u u的回文串
再以第一个 w r w^r wr和第二个 w w w的中心为轴,肯定有一个长度不低于 2 u 2u 2u的回文串
a i a_i ai表示以 i i i i + 1 i+1 i+1的缝隙为对称轴的回文串的长度
那么一个 w w r w w r ww^rww^r wwrwwr就体现在 a a a序列中有两个数 a j , a j ( i &lt; j ) a_j,a_j(i&lt;j) aj,aj(i<j),满足 a i ≥ u a_i\geq u aiu a j ≥ 2 u a_j \geq 2u aj2u,且 j − i = u j-i=u ji=u
那么现在就是序列问题了
我如果枚举 i i i,那么就成了找满足 a i ≥ j − i a_i \geq j-i aiji a j ≥ 2 j − 2 i a_j \geq 2j-2i aj2j2i的最大的 j j j
化简一下得, i ≤ j ≤ i + a i i\leq j \leq i+a_i iji+ai 2 j − a j ≤ 2 i 2j-a_j \leq 2i 2jaj2i
其实就是在 [ i , i + a i ] [i,i+a_i] [i,i+ai]这个区间去找满足条件 2 j − a j ≤ 2 i 2j-a_j\leq 2i 2jaj2i的最大的 j j j
因为 2 i 2i 2i单调增加,所以我可以把所有的 j j j按照 2 j − a j 2j-a_j 2jaj排序,用一个指针每次往后移动一下把符合条件的 j j j加到树状数组中去,每次查询 [ 1 , i + a i ] [1,i+a_i] [1,i+ai]的前缀最小值,如果最小值 x x x不小于 i i i,那么就用 u = x − i u=x-i u=xi更新答案

代码

#include <bits/stdc++.h>
#define maxn 600010
#define lowbit(x) ((x)&-(x))
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
struct Manacher
{
    int r[maxn], p[maxn], n;
    void clear(){cl(r), cl(p);}
    void calc(int *s, int N)
    {
        n=N;
        int i, j, mx(0), center;
        r[0]=-2;
        for(i=1;i<=N;i++)r[2*i]=s[i];
        for(i=1;i<=N;i++)r[2*i-1]=-1;
        r[2*N+1]=-1;
        for(i=1;i<=2*N+1;i++)
        {
            if(mx>=i)p[i]=min(p[2*center-i],mx-i+1);
            else p[i]=1;
            while(r[i-p[i]]==r[i+p[i]])p[i]++;
            if(i+p[i]-1>mx)
            {
                mx=i+p[i]-1;
                center=i;
            }
        }
    }
}mnc;
struct BIT
{
    int a[maxn], n;
    void clear(){cl(a);}
    void add(int pos, int v)
    {
        for(;pos<=n;pos+=lowbit(pos))a[pos]=max(a[pos],v);
    }
    int pre_max(int pos)
    {
        int ans(0);
        for(;pos;pos-=lowbit(pos))ans=max(ans,a[pos]);
        return ans;
    }
}bit;
char s[maxn];
int len, r[maxn], a[maxn];
void init()
{
    int i;
    scanf("%s",s+1);
    len=strlen(s+1);
    cl(r);
    for(i=1;i<=len;i++)r[i]=s[i];
    mnc.clear();
    mnc.calc(r,len);
}
vector< pair<int,int> > d;
void work()
{
    int i, j, x, ans(0), p(0);

    for(i=1;i<=len;i++)
        a[i]=(mnc.p[i*2+1]-1)/2;
    
    d.clear();
    for(j=1;j<=len;j++)
        d.emplace_back( make_pair(j-a[j]/2,j) );
    sort(d.begin(),d.end());
    
    bit.clear();
    bit.n=len;
    for(i=1;i<=len;i++)
    {
        while(p<d.size())
        {
            if(d.at(p).first <= i)
            {
                bit.add(d.at(p).second,d.at(p).second);
                p++;
            }
            else break;
        }
        j=bit.pre_max(i+a[i]);
        if(j>=i)ans=max(ans,j-i);
    }

    printf("%d\n",ans*4);
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        init();
        work();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值