bzoj3160 万径人踪灭(FFT+manacher)

题意:已知一个长度为n的字符串仅由’a’和’b’构成,求该串不连续的回文子串个数。
严格地描述,题意中的回文子串应满足:
1.位置字符都关于某条线对称
2.该回文串不连续
解法:FFT+manacher
1.先求连续区间内的回文子串个数ans1。用manacher算法可以得到
2.再求所有连续子串个数。将字符串增倍(每个字符之间加入#字符),设f[i]:在倍增串中以第i个字符为中心且出现在原字符串中的对称字符的对数。
结论:f[i]= ((len1j=0ch[j]==ch[ij])+1)>>1
即在倍增串中以第i位字符为中心时,原串中下标和为i的相同字符对f[i]有贡献。
3.以倍增串中第i个字符为对称轴的对称回文串个数为 2f[i] -1
4.ans= ((2f[i]1))ans1

#include <stdio.h>
#include <string.h>
#include <set>
#include <map>
#include <algorithm>
#include<complex>
#include<iostream>
#define pi acos(-1)
#define ll long long
using namespace std ;
typedef complex<double> E;

const int mod = 1000000007;
const int maxn = 300000+10;

char ch[maxn];
int len;
E a[maxn],b[maxn];//a是乘积前的系数数组 b是乘积后的系数数组 赋值到c数组中
int n,m,L;
int R[maxn],c[maxn],p[maxn];
ll ans,bin[maxn];

void fft(E *a,int f){
    for(int i=0;i<n;i++) if(i<R[i]) swap(a[i],a[R[i]]);
    for(int i=1;i<n;i<<=1){
        E wn(cos(pi/i),f*sin(pi/i));
        for(int p=i<<1,j=0;j<n;j+=p){
            E w(1,0);
            for(int k=0;k<i;k++,w*=wn){
                E x=a[j+k],y=w*a[j+k+i];
                a[j+k]=x+y; a[j+k+i]=x-y;
            }
        }
    }
    if(f==-1) for(int i=0;i<n;i++) a[i]/=n;
}

void manacher(){
    ch[0]='-',ch[len+1]='+';//字符串首尾得空出来!
    int id,mx=0;
    memset(p,0,sizeof(p));
    for(int i=1;i<=len;i++){
        if(mx>i) p[i]=min(mx-i+1,p[2*id-i]);
        else p[i]=1;
        while(ch[i+p[i]]==ch[i-p[i]]) p[i]++;
        if(i+p[i]-1>mx) mx=i+p[i]-1,id=i;
        ans=((ans-p[i])%mod+mod)%mod;
    }
    mx=0;
    for(int i=1;i<=len;i++){
        if(mx>i) p[i]=min(mx-i,p[2*id-i]);
        else p[i]=0;
        while(ch[i+p[i]+1]==ch[i-p[i]]) p[i]++;
        if(i+p[i]>mx) mx=i+p[i],id=i;
        ans=((ans-p[i])%mod+mod)%mod;
    }
}

int main(){
        //freopen("a.txt","r",stdin);
        //求2的i次幂
        bin[0]=1; for(int i=1;i<=maxn;i++) bin[i]=(bin[i-1]*2)%mod;
        scanf("%s",ch+1);
        len=strlen(ch+1);
        ans=0;
        //先求连续的回文子串的个数
        manacher();
        //初始化n,m,l,R[]
        n=len-1; m=2*n; L=0;
        for(n=1;n<=m;n<<=1) L++;
        for(int i=1;i<n;i++) R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
        //初始化a多项式系数数组 并求FFT和a*a多项式值表达式
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        for(int i=0;i<len;i++) a[i]=(ch[i+1]=='a');
        fft(a,1);
        for(int i=0;i<=n;i++) b[i]=a[i]*a[i];

        //重新初始化a多项式系数数组 并求FFT和a*a多项式值表达式
        memset(a,0,sizeof(a));
        for(int i=0;i<len;i++) a[i]=(ch[i+1]=='b');
        fft(a,1);
        for(int i=0;i<=n;i++) b[i]+=a[i]*a[i];
        //求FFT逆变换 并求各幂次对应系数
        fft(b,-1);
        for(int i=0;i<=m;i++) c[i]=b[i].real()+0.1;
        //求结果
        for(int i=0;i<=m;i++)
            ans=((ans+bin[(c[i]+1)>>1]-1)%mod+mod)%mod;
        printf("%lld\n",(ans+mod)%mod);
        return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值