zoukankan      html  css  js  c++  java
  • bzoj 3160

    首先简化一下题意:

    求一个字符串的子序列个数,要求这个子序列满足:是一个回文序列,且在原串中不连续

    怎么搞?

    设这个字符串为S

    首先上一个容斥:我们找出所有回文子序列,然后减去连续的部分即可

    而连续的部分可以用manacher算出来

    所以我们重点研究一下如何找出所有回文子序列

    首先我们回到manacher算法的思想:如果我想找出回文子序列,那么我们不如去找对称轴!(在接下来的介绍中不区分对称轴和对称中心)

    而同理,对称轴会有两种情况:以一个字符为对称中心的和以一个空位为对称中心的

    所以我们分类讨论:

    ①.设我们以位置$i$上的字符为对称轴,那么如果我们能找到两个位置$x,y$,使得$x+y=2*i$,且$S_x==S_y$,那么很显然,选出$(S_x,S_i,S_y)$这一个子序列是合法的

    那么,如果我们能找到k组这样的x,y,那么以位置i上字符为对称轴的方案就是$2^(k+1)-1$

    这一点很好理解,因为算上中间的i,一共有$k+1$组可以支持选或不选,但是要减去所有都不选的情况

    ②.设我们以位置$i$与$i+1$之间的空格为对称轴,那么如果我们能找到两个位置$x,y$,满足$x+y=2*i+1$,且$S_x==S_y$,那么很显然,选出$(S_x,S_y)$这一个子序列是合法的

    那么,如果我们能找到k组这样的x,y,那么以位置i与i+1之间空格为对称轴的方案就是$2^k-1$

    我们看到,设字符串长度为$n$,如果有$S_x==S_y$且对称轴为i(假设以一个字符对称),那么一定有$S_x==S_2*i-x$!

    那么,由于只有a,b两种字符,所以我们可以分开匹配a,b两种字符

    在匹配a字符时,我们可以设所有为a的位置值为1,而所有为b的位置值为0,那么可以发现,如果$S_x*S_2*i-x==1$,那么就可以说明这两个位置上是对应的a!

    那么这是不是有点类似卷积的形式了?

    可以发现,如果我们按上述说法将字符串转成一个序列,然后对这个序列自己与自己卷积,会得到一个长度为2*n的序列,其中下标为奇数的就是初始对称轴为空格的情况,下标为偶数的就是初始对称轴为一个字符的情况

    那么做法是不就很显然了?

    我们按a对字符串转成一个序列与自己卷积,记录对应位置的答案,再按b对字符串转成一个序列与自己卷积,将对应位置的答案累计上去,然后每一位分奇偶算2的幂次-1,最后减掉manacher做出的连续部分即可

    注意在累计时,由于每一组对应的$S_x$与$S_2*i-x$在卷积时事实上被记录了两次,所以每一个位置都应该除2再做2的幂次!!

    贴代码:

    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <cstdlib>
    #include <iostream>
    #include <algorithm>
    #include <queue>
    #include <stack>
    #define ll long long
    using namespace std;
    const ll mode=1000000007;
    const double pi=acos(-1.0);
    int to[(1<<20)+5];
    struct cp
    {
        double x,y;
    };
    cp operator + (cp a,cp b)
    {
        return (cp){a.x+b.x,a.y+b.y};
    }
    cp operator - (cp a,cp b)
    {
        return (cp){a.x-b.x,a.y-b.y};
    }
    cp operator * (cp a,cp b)
    {
        return (cp){a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x};
    }
    void FFT(cp *a,int len,int k)
    {
        for(int i=0;i<len;i++)if(i<to[i])swap(a[i],a[to[i]]);
        for(int i=1;i<len;i<<=1)
        {
            cp w0=(cp){cos(pi/i),k*sin(pi/i)};
            for(int j=0;j<len;j+=(i<<1))
            {
                cp w=(cp){1,0};
                for(int o=0;o<i;o++,w=w*w0)
                {
                    cp w1=a[j+o];
                    cp w2=a[j+o+i]*w;
                    a[j+o]=w1+w2;
                    a[j+o+i]=w1-w2;
                }
            }
        }
    }
    char ch[100005];
    char temp[200005];
    int p[200005];
    ll f[200005];
    int n,maxp,p0;
    ll sum=0;
    int lim=1,l;
    void manacher()
    {    
        temp[0]=temp[1]='#';
        for(int i=1;i<=n;i++)temp[i<<1]=ch[i],temp[(i<<1)|1]='#';
        p[0]=1;
        int l=(n<<1)+2;
        temp[l]='0';
        for(int i=1;i<l;i++)
        {
            if(i<maxp)p[i]=min(p[(p0<<1)-i],p[p0]+p0-i);
            else p[i]=1;
            while(temp[i-p[i]]==temp[i+p[i]])p[i]++;
            if(p[i]+i>maxp)maxp=p[i]+i,p0=i;
        }
        for(int i=1;i<=l;i++)sum+=p[i]/2;
    }
    cp a[(1<<20)+5],b[(1<<20)+5],c[(1<<20)+5];
    ll pow_mul(ll x,ll y)
    {
        ll ans=1;
        while(y)
        {
            if(y&1)ans*=x,ans%=mode;
            x*=x,x%=mode,y>>=1;
        }
        return ans;
    }
    int main()
    {
        scanf("%s",ch+1);
        n=strlen(ch+1);
        manacher();
        for(int i=1;i<=n;i++)a[i-1].x=(ch[i]=='a');
        while(lim<=2*n)lim<<=1,l++;
        for(int i=1;i<lim;i++)to[i]=((to[i>>1]>>1)|((i&1)<<(l-1)));
        FFT(a,lim,1);
        for(int i=0;i<lim;i++)c[i]=a[i]*a[i];
        FFT(c,lim,-1);
        for(int i=0;i<2*n-1;i++)f[i]+=(ll)(c[i].x/lim+0.5);
        for(int i=1;i<=n;i++)b[i-1].x=(ch[i]=='b');
        FFT(b,lim,1);
        for(int i=0;i<lim;i++)c[i]=b[i]*b[i];
        FFT(c,lim,-1);
        for(int i=0;i<2*n-1;i++)f[i]+=(ll)(c[i].x/lim+0.5);
        ll ans=0;
        for(int i=0;i<2*n-1;i++)
        {
            if(i&1)ans+=pow_mul(2,f[i]/2)-1,ans%=mode;
            else ans+=pow_mul(2,f[i]/2+1)-1,ans%=mode;
        }
        printf("%lld
    ",((ans-sum)%mode+mode)%mode);
        return 0;
    }
  • 相关阅读:
    腾讯广告算法大赛2019
    Mysql的部分常用SQL语句
    org.activiti.dependencies 7.1.0.M6 造成版本冲突问题的解决
    windows 将 redis 注册为服务 自动启动或手动启动
    org.springframework.security.access.AccessDeniedException: Access is denied
    对两个List进行关联匹配,选择匹配上的记录形成新的List输出
    越是大型的组织,越需要试验基地,试验基地应有特殊待遇
    dubbo+zookeeper 安装所遇系列问题
    签名与验签——图解
    关于航空母舰战斗群出海训练,常有大量鱼群跟随问题的建议和设想
  • 原文地址:https://www.cnblogs.com/zhangleo/p/10802668.html
Copyright © 2011-2022 走看看