• [BZOJ3790]神奇项链(manacher)


    题目

    Description

    母亲节就要到了,小 H 准备送给她一个特殊的项链。这个项链可以看作一个用小写字母组成的字符串,每个小写字母表示一种颜色。为了制作这个项链,小 H 购买了两个机器。第一个机器可以生成所有形式的回文串,第二个机器可以把两个回文串连接起来,而且第二个机器还有一个特殊的性质:假如一个字符串的后缀和一个字符串的前缀是完全相同的,那么可以将这个重复部分重叠。例如:aba和aca连接起来,可以生成串abaaca或 abaca。现在给出目标项链的样式,询问你需要使用第二个机器多少次才能生成这个特殊的项链。 

    Input

    输入数据有多行,每行一个字符串,表示目标项链的样式。 
    每个测试数据,输入不超过 5行 
    每行的字符串长度小于等于 50000 

    Output

    多行,每行一个答案表示最少需要使用第二个机器的次数。

    Sample Input

    abcdcba 
    abacada 
    abcdef 

    Sample Output

    0
    2
    5

    思路:

    首先你必须知道基础的$manacher$算法;

    那么这一题怎么写呢;

    题目可以转化为多少个回文串可以覆盖全部;

    很容易想到先把每个回文串的最左节点和最右节点求出来;

    然后把每个回文串按左节点排序,如果左节点一样,最好按右节点大的排在前面;

    这样就方便找覆盖最大的回文串,并且全部覆盖;

    然后再贪心,第一次加入左节点为1并且右节点延伸最远的回文串;

    之后加入的数,我们需要中间不留空隙,所以加入的回文串需要与之前的回文串重合(为什么重合,看题!!)或相邻,且向右延伸最远;

    如题目样例的第二个:

    abacada

    第一次加入aba;左节点为1且向右延伸最远的串;

    第二次加入aca;与前面串重合且向右延伸最远的串;

    第三次加入ada;....................................................

    根据题意需要连接两次,所以答案是2;


    代码:

    #include<bits/stdc++.h>
    typedef long long ll;
    using namespace std;
    inline ll read()
    {
        ll a=0,f=1; char c=getchar();
        while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
        while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
        return a*f;
    }
    char s[1000010],snew[2000010];
    ll n,p[2000010];
    struct ljq
    {
        ll L,R;
    }f[4000010];
    inline ll build()
    {
        snew[0]='$';snew[1]='#';
        ll j=2,len=strlen(s);
        for(ll i=0;i<len;i++)
        {
            snew[j]=s[i];j++;
            snew[j]='#'; j++;//* 
        }
        snew[j]='';//防越界标志 
        n=j-1;//n是修改过后字符串的长度 
        return j;
    }
    inline void manacher()
    {
        ll len=build();
        ll id=0,mx=0;
        for(ll i=1;i<len;i++)//为什么是i<len而不是i<=len呢;
                            //在build函数中j在赋值之后 又加了一次 如 * 
        {
            ll j=2*id-i;
            if(i<mx)
                p[i]=min(p[j],mx-i);
            else
                p[i]=1;
            while(snew[i+p[i]]==snew[i-p[i]])
                p[i]++;
            if(p[i]+i>mx)
            {
                id=i;
                mx=p[i]+i;
            }//上面为manacher的基本sao操作; 
            f[i].L=i-(p[i]-1);
            f[i].R=i+(p[i]-1);//记录好每一个回文串的端点; 
        }
    }
    inline ll cmp(ljq a,ljq b)
    {
        if(a.L==b.L)
            return a.R>b.R;
        else
            return a.L<b.L;
    }//排序,为什么这样排,看思路 
    int main()
    {
        while(scanf("%s",s)!=EOF)
        {
            memset(p,0,sizeof(p));
            memset(f,0,sizeof(f));
            manacher();
            sort(f+1,f+n+1,cmp);
    //        cout<<endl;
    //        for(ll i=1;i<=n;i++)
    //            cout<<f[i].L<<" "<<f[i].R<<endl;
            if(f[1].L==1&&f[1].R==n)
            {
                printf("0
    ");
                continue;
            }//因为排序按覆盖最长的排序,所以f[1]代表整个回文的最长覆盖;
             //如果整个字符就是回文串,那么就不需要连接了,直接输出0; 
            ll ans=0;
            ll i=1,pre=0;
            while(i<=n)
            {
                ll sum=0;//pre表示当前加入的最后一个回文串的右端点 ; 
                while(f[i].L-1<=pre)//为了防止中间不留空隙,每个回文必须相连 ; 
                { 
                    sum=max(sum,f[i].R);//找到延伸最远的回文串; 
                    i++;
                }
                ans++;
                pre=sum;//记下右端点 
                if(pre==n)//如果全部覆盖了,那么退出 
                    break;
            }
            printf("%lld
    ",ans-1);//因为第一次加回文的时候,ans++了,而题目是求相连的次数,所以要减一 
        }
        return 0;
    }

    作者博客园:木偶人-怪咖(防伪)

  • 相关阅读:
    [转载]四大Java EE容器
    [转载]javaEE规范和SSH三大框架到底有什么关系
    javaee包含的服务和组件
    Java类文件最大限制
    oracle给字段添加描述
    apache commons工具包
    redis教程
    git学习
    编程人物
    程序员必须了解的5大编程准则
  • 原文地址:https://www.cnblogs.com/wzx-RS-STHN/p/13366132.html
Copyright © 2020-2023  润新知