BZOJ[3160]万径人踪灭 FFT+Manacher

8 篇文章 0 订阅
3 篇文章 0 订阅

传送门ber~

答案 ans= a n s = 所有回文子串个数 连续的回文子串个数
现在这两个分别考虑

i为对称轴的对称字符个数 f(i)=j=1i(strj==str2ij) f ( i ) = ∑ j = 1 i ( s t r j == s t r 2 ∗ i − j ) ,那么以 i i 为对称轴的对称子串个数为2f(i)1
我日这怎么这么像卷积??
将“a”,“b”分别视作1,0
那么 f(i)=j=1iaja2ij f ( i ) = ∑ j = 1 i a j ∗ a 2 ∗ i − j
我日真的是卷积!
FFT一发

后面那个Manacher显然

代码如下:

#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#define MOD 1000000007
#define N 2000020
using namespace std;
const double DFT=2.0,IDFT=-2.0;
const double pi=acos(-1);
struct Complex{
    double x,y;
    Complex(double _=0.0,double __=0.0):x(_),y(__){}
    Complex operator + (Complex b) {return Complex(x+b.x,y+b.y);}
    Complex operator - (Complex b) {return Complex(x-b.x,y-b.y);}
    Complex operator * (Complex b) {return Complex(x*b.x-y*b.y,x*b.y+b.x*y);}
}a[N],b[N];
char s[N],str[N];
int n,len,len1,id,mx;
int pos[N],p[N];
long long q[N];
long long ans;
inline void FFT(Complex x[],double mode){
    for(int i=0;i<len;i++)
        if(i<pos[i])
            swap(x[i],x[pos[i]]);
    for(int i=2,mid=1;i<=len;i<<=1,mid<<=1){
        Complex wm(cos(2.0*pi/i),sin(mode*pi/i));
        for(int j=0;j<len;j+=i){
            int r=j+mid;
            Complex w(1,0);
            for(int k=j;k<r;k++,w=w*wm){
                Complex y=x[k],z=w*x[k+mid];
                x[k]=y+z;x[k+mid]=y-z;
            }
        }
    }
    if(mode==IDFT)
        for(int i=0;i<len;i++)
            x[i].x/=len;
    return;
}
inline void solve(char c){
    for(int i=0;i<len;i++) a[i]=Complex(0,0);
    for(int i=0;i<len;i++)
        if(s[i]==c)
            a[i].x=1;
    FFT(a,DFT);
    for(int i=0;i<len;i++)
        b[i]=b[i]+a[i]*a[i];
    return;
}
inline long long Manacher(){
    str[0]='#';
    for(int i=0;i<n;i++){
        str[i*2+1]=s[i];
        str[i*2+2]='#';
    }
    len1=n*2;
    str[++len1]='%';
    id=mx=0;
    for(int i=0;i<=len1;i++){
        if(i<mx) p[i]=min(mx-i,p[id*2-i]);
        else p[i]=0;
        while(str[i-p[i]-1]==str[i+p[i]+1])
            p[i]++;
        if(i+p[i]>mx) mx=i+p[i],id=i;
    }
    long long tmp=0;
    for(int i=0;i<len1;i++)
        tmp=(tmp+(p[i]+1)/2)%MOD;
    return tmp;
}
int main(){
    scanf("%s",s);
    n=strlen(s);
    q[0]=1;
    for(int i=1;i<=n;i++) q[i]=q[i-1]*2%MOD;
    for(len=1;len<n<<1;len<<=1);
    for(int i=0;i<len;i++){
        pos[i]=pos[i>>1]>>1;
        if(i&1) pos[i]|=len>>1;
    }
    solve('a');solve('b');
    FFT(b,IDFT);
    for(int i=0;i<=len;i++)
        ans=(ans+(q[int(floor(b[i].x+0.5)+1)>>1]-1)%MOD)%MOD;
    long long t=Manacher();
    ans=(ans-t+MOD)%MOD;
    printf("%lld",ans);
return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值