【bzoj3790】神奇项链——manacher+贪心

听说这道题可以用树状数组什么鬼的做,反正我不会,还是老老实实打manacher+贪心大法吧……

我们只要在跑manacher的过程中,用一个结构体(也可以直接用数组)来记录以每个字符为对称轴的最长回文的最左端和最右端,然后就得到了一些线段,于是问题完美地转换成了求取最少段的线段来完全覆盖一个区间了,直接排完序后贪心一遍即可。当然这里面有些比较是不必要的,但我也懒得去优化了。

具体实现细节看代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
char s[100010];
int p[100010],pos,cnt;
struct point
{
    int begin,end;
}a[100010];
bool cmp(point b,point c)
{
    return b.begin<c.begin||(b.begin==c.begin&&b.end>c.end);
}
int main()
{
    while(~scanf("%s",s))
    {
        memset(p,0,sizeof(p));
        int len=strlen(s);
        pos=0;cnt=0;
        for(int i=len;i>=0;i--)
        {
            s[i*2+2]=s[i];
            s[i*2+1]='#';
        }
        s[0]='$';
        for(int i=2;i<len*2+1;i++)
        {
            if(pos+p[pos]>i)p[i]=min(p[pos*2-i],pos+p[pos]-i);
            else p[i]=1;
            while(s[i+p[i]]==s[i-p[i]])p[i]++;
            if(pos+p[pos]<i+p[i])pos=i;
            a[++cnt].begin=i-p[i]+1;a[cnt].end=i+p[i]-1;
        }
        sort(a+1,a+1+cnt,cmp);
        int ans=0,r=a[1].end,now=2;
        while(r<len*2)
        {
            int r0=r;
            while(now<=cnt&&a[now].begin<=r)
            {
                r0=max(r0,a[now].end);
                now++;
            }
            ans++;r=r0;
        }
        printf("%d\n",ans);
    }
    return 0;
}
View Code

 

posted @ 2017-06-07 23:32  Child-Single  阅读(257)  评论(0编辑  收藏  举报