[压位 || FFT] 【Hillan 的题】简单字符串匹配

不匹配度就是异或后数1的个数。可以压位优化一下,需要对于任意为开始,每 16 压一起。预处理 216 以内的数中1的个数。
这样可以优化到 O(nQ/16) ,是可以过的。

对于最后1分的包,考虑答案为 (S1iS2j)2=S12i+S22i2S1iS2j
只需求 Fi=i+N1j=iS1jS2j ,然后变形一下,把 S1 反一下:

Fi=j=0i+n1S1ijS2j
然后 FFT

#include<cstdio>
#include<cmath>
#include<cstring>
#include<bitset>
#include<algorithm>
using namespace std;
const int maxn=2000005;
inline char gc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int getint(){
    char ch=gc(); int res=0;
    while(!('0'<=ch&&ch<='9')) ch=gc();
    while('0'<=ch&&ch<='9') res=(res<<1)+(res<<3)+ch-'0', ch=gc();
    return res;
}
int n,m,Q,last_ans,b1[18][maxn],b2[18][maxn],cnt[(1<<16)+5],S1[maxn],S2[maxn],sum1,sum2[maxn];
int Get(int a[],int L,int R){
    int res=0; for(int i=L;i<=R;i++) res|=(a[i]<<i-L);
    return res;
}
void Pre(){
    for(int i=0;i+15<=n;i+=16) b1[0][i/16]=Get(S1,i,i+15);
    for(int j=1;j<=15;j++)
     for(int i=j;i+15<=n;i+=16) 
     b1[j][i/16]=b1[j-1][i/16]>>1|(S1[i+15]<<15);
    for(int i=0;i+15<=m;i+=16) b2[0][i/16]=Get(S2,i,i+15);
    for(int j=1;j<=15;j++)
     for(int i=j;i+15<=m;i+=16) b2[j][i/16]=b2[j-1][i/16]>>1|(S2[i+15]<<15);
    for(int i=1;i<=(1<<16)-1;i++){
        int t=i; do cnt[i]+=t&1, t>>=1; while(t); 
    }
}
int Solve(int p1,int p2,int len){
    int res=0,now;
    for(now=1;now+15<=len;now+=16) 
    res+=cnt[b1[(p1+now-1)%16][(p1+now-1)/16]^b2[(p2+now-1)%16][(p2+now-1)/16]];
    for(;now<=len;now++) res+=S1[p1+now-1]^S2[p2+now-1];
    return res;
}
const double PI=acos(-1);
struct E{
    double real,imag;
    E(double t1=0,double t2=0){ real=t1; imag=t2; }
    void operator /= (const int &val){ real/=val; imag/=val; }
};
E operator + (const E &A,const E &B){ return E(A.real+B.real,A.imag+B.imag); }  
E operator - (const E &A,const E &B){ return E(A.real-B.real,A.imag-B.imag); }  
E operator * (const E &A,const E &B){ return E(A.real*B.real-A.imag*B.imag,A.imag*B.real+A.real*B.imag); }
E operator / (const E &A,const int &x){ return E(A.real/x,A.imag/x); }
int rev[maxn];
void get_rev(int n){
    rev[0]=0; int log2n=log2(n);
    for(int i=1;i<=n-1;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(log2n-1));
}
void FFT(E a[],int n,int _k){
    for(int i=0;i<=n-1;i++) if (i<rev[i]) swap(a[i],a[rev[i]]);
    for(int m=2;m<=n;m<<=1){
        E wm(cos(2*PI/m),_k*sin(2*PI/m));
        for(int k=0;k<=n-1;k+=m){
            E w(1,0),t0,t1;
            for(int j=0;j<=m/2-1;j++,w=w*wm) t0=a[k+j], t1=w*a[k+j+m/2], a[k+j]=t0+t1, a[k+j+m/2]=t0-t1;    
        } 
    }
    if(_k==-1) for(int i=0;i<=n-1;i++) a[i]/=n;
}
E _S1[maxn],_S2[maxn],F[maxn];
int main(){
    freopen("hhhoj42.in","r",stdin);
    freopen("hhhoj42.out","w",stdout);
    char ch=gc(); while(ch!='0'&&ch!='1') ch=gc();
    for(int i=0;ch=='0'||ch=='1';ch=gc(),i++) S1[i]=ch-'0', n++; 
    ch=gc(); while(ch!='0'&&ch!='1') ch=gc();
    for(int i=0;ch=='0'||ch=='1';ch=gc(),i++) S2[i]=ch-'0', m++; 
    if(n>=300000){
        for(int i=0;i<=n-1;i++) sum1+=S1[i]*S1[i];
        for(int i=0;i<=m-1;i++) sum2[i]=(i-1>=0?sum2[i-1]:0)+S2[i]*S2[i];
        for(int i=0;i<=n-1;i++) _S1[i]=S1[i];
        for(int i=0;i<=m-1;i++) _S2[i]=S2[i];
        for(int i=0;i<=(n-1)/2;i++) swap(_S1[i],_S1[n-1-i]);
        int _m=1; while(_m<m) _m<<=1; 
        get_rev(_m); FFT(_S1,_m,1); FFT(_S2,_m,1);
        for(int i=0;i<=_m-1;i++) F[i]=_S1[i]*_S2[i];
        FFT(F,_m,-1);
        Q=getint();
        while(Q--){
            int p1=getint(),p2=getint(),len=getint(); p2=(p2^last_ans)%(m-len+1);
            int res=(F[p2+n-1].real+0.1);
            printf("%d\n",last_ans=sum1+sum2[p2+n-1]-(p2-1>=0?sum2[p2-1]:0)-2*res);
        }
        return 0;
    }
    Pre();
    Q=getint();
    while(Q--){
        int p1=getint(),p2=getint(),len=getint(); p2=(p2^last_ans)%(m-len+1);
        printf("%d\n",last_ans=Solve(p1,p2,len));
    }
    return 0; 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值