ABC196 F - Substring 2(NTT)

题意:

在这里插入图片描述

解法:

设 s [ 0 , n ] , t [ 0 , m ] . 令 f ( x ) = ∑ i = 0 m ( s [ x + i ] − t [ i ] ) 2 , 显 然 f ( x ) 是 以 s [ x ] 为 开 头 , 与 t 串 匹 配 需 要 的 修 改 位 数 . 将 式 子 展 开 可 得 : f ( x ) = ∑ i = 0 m s [ x + i ] 2 + ∑ i = 0 m t [ i ] 2 − 2 ∑ i = 0 m s [ x + i ] ∗ t [ i ] . 前 两 项 用 前 缀 和 就 能 计 算 出 来 , 第 三 项 g ( x ) = ∑ i = 0 m s [ x + i ] ∗ t [ i ] , 将 t 串 反 转 , 那 么 式 子 变 为 : g ( x ) = ∑ i = 0 m s [ x + i ] ∗ t [ m − i ] 变 成 了 卷 积 形 式 , 可 以 用 F F T 或 者 N T T 计 算 g ( x ) = s ( ) 和 t ( ) 卷 积 结 果 的 第 x + m 项 设s[0,n],t[0,m].\\ 令f(x)=\sum_{i=0}^{m}(s[x+i]-t[i])^2,\\ 显然f(x)是以s[x]为开头,与t串匹配需要的修改位数.\\ 将式子展开可得:\\ f(x)=\sum_{i=0}^{m}s[x+i]^2+\sum_{i=0}^{m}t[i]^2-2\sum_{i=0}^{m}s[x+i]*t[i].\\ 前两项用前缀和就能计算出来,\\ 第三项g(x)=\sum_{i=0}^{m}s[x+i]*t[i],\\ 将t串反转,那么式子变为:\\ g(x)=\sum_{i=0}^{m}s[x+i]*t[m-i]\\ 变成了卷积形式,可以用FFT或者NTT计算\\ g(x)=s()和t()卷积结果的第x+m项 s[0,n],t[0,m].f(x)=i=0m(s[x+i]t[i])2,f(x)s[x],t.:f(x)=i=0ms[x+i]2+i=0mt[i]22i=0ms[x+i]t[i].,g(x)=i=0ms[x+i]t[i],t,:g(x)=i=0ms[x+i]t[mi],FFTNTTg(x)=s()t()x+m

code:
#include<bits/stdc++.h>
using namespace std;
const int maxm=4e6+5;
const int mod=998244353;
// const int mod=1004535809;
// const int mod=469762049;
const int G=3;
int rev[maxm];
char s[maxm];
int a[maxm];
int b[maxm];
int sum1[maxm];
int sum2[maxm];
int sum3[maxm];
int n,m;
int ppow(int a,int b,int mod){
    int ans=1%mod;a%=mod;
    while(b){
        if(b&1)ans=1ll*ans*a%mod;
        a=1ll*a*a%mod;
        b>>=1;
    }
    return ans;
}
void ntt(int a[],int len,int on){
    for(int i=0;i<len;i++)if(i<rev[i])swap(a[i],a[rev[i]]);
    for(int i=1;i<len;i<<=1){
        int wn=ppow(G,(mod-1)/(i<<1),mod);
        if(on==-1)wn=ppow(wn,mod-2,mod);
        for(int j=0;j<len;j+=(i<<1)){
            int w=1;
            for(int k=0;k<i;k++){
                int x=a[j+k],y=1ll*w*a[i+j+k]%mod;
                a[j+k]=((1ll*x+y)%mod+mod)%mod;
                a[i+j+k]=(1ll*x-y+mod)%mod;
                w=1ll*w*wn%mod;
            }
        }
    }
    if(on==-1){
        int inv=ppow(len,mod-2,mod);
        for(int i=0;i<len;i++)a[i]=1ll*a[i]*inv%mod;
    }
}
signed main(){
    //
    scanf("%s",s);
    n=strlen(s);n--;
    for(int i=0;i<=n;i++)a[i]=s[i]-'0';
    sum1[0]=a[0]*a[0];
    for(int i=1;i<=n;i++)sum1[i]=sum1[i-1]+a[i]*a[i];
    //
    scanf("%s",s);
    m=strlen(s);m--;
    for(int i=0;i<=m;i++)b[i]=s[i]-'0';
    sum2[0]=b[0]*b[0];
    for(int i=1;i<=m;i++)sum2[i]=sum2[i-1]+b[i]*b[i];
    //
    reverse(b,b+m+1);
    int len=1,l=0;
    while(len<=m+n)len<<=1,l++;
    for(int i=0;i<len;i++){
        rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));
    }
    ntt(a,len,1);
    ntt(b,len,1);
    for(int i=0;i<len;i++)a[i]=1ll*a[i]*b[i]%mod;
    ntt(a,len,-1);
    len=n+m;
    for(int i=0;i<=n+m;i++){
        sum3[i]=a[i];
    }
    int ans=1e9;
    for(int i=0;i<=n-(m+1)+1;i++){
        int t=0;
        t+=sum1[i+m]-(i==0?0:sum1[i-1]);
        t+=sum2[m];
        t-=2*sum3[i+m];
        ans=min(ans,t);
    }
    cout<<ans<<endl;
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值