• 【洛谷4173】残缺的字符串(重拾FFT)


    点此看题面

    大致题意: 有一个长度为(n)的字符串(A)和一个长度为(m)的字符串(B),其中存在一些字符'*'可以与任意字符匹配。求(B)中所有满足条件的位置,使得从这一位置开始的连续(n)个字符组成的字符串能与(A)完全匹配。

    前言

    之前一直对(FFT)这个算法不太理解,最近感觉数学水平有了点小提升,于是去重新看了遍(FFT)的原理,突然发现能大致理解了......

    这道题原题是(A)长度为(m)(B)长度为(n),出于个人习惯,把(n)(m)调换了一下,变成(A)长度为(n)(B)长度为(m)

    同样出于个人习惯,我还把(n)(m)各自减了(1),则(A)中下标为(0sim n)(B)中下标为(0sim m),由于题目要求下标从(1)开始,所以最后又将答案加了(1)

    希望能理解。

    (FFT)与字符串匹配

    (FFT)与字符串匹配,看似没有任何联系,实际上却大有用处。

    例如这题,首先我们可以把字符转化成数字,'a'~'z'各自对应(1sim 26),而'*'就用(0)来表示。

    先不考虑'*',此时如果要比较两个字符(x,y)是否匹配,显然我们可以发现若(x=y),即(x-y=0),两个字符匹配,而若(x-y≠0),则两个字符不匹配。

    也就是说,我们这里把(x-y)看作是匹配值,匹配值为(0)表示匹配,不为(0)表示不匹配。

    但是,如果要比较两个字符串是否匹配,却不能单纯把每一位上(x-y)的值加一起来判断是否(0),因为(x-y)可正可负,加一起可能会相互抵消成(0)

    怎么办?

    把负号去掉不就行了吗?首先便会想到把匹配值改为(|x-y|),但这似乎不太好操作。

    但是,如果改为((x-y)^2),似乎就很可行的样子。

    总结一下,要比较两个长度相等的字符串是否完全匹配,我们可以对于每一位求出((x-y)^2),然后求和,如果最后的和为(0),说明完全匹配,否则说明不匹配。

    那出现了'*'呢?

    由于'*'可以与任意字符匹配,且上面我们决定了用(0)来表示''*',而完全匹配的定义又是匹配值为(0)

    所以,我们可以把匹配值改为(xy(x-y)^2),如果这个式子求和为(0),就说明完全匹配。

    推式子

    假设我们现在要判断从(B)中第(i)位开始的连续(n)个字符是否能与(A)完全匹配。

    则就是要求:

    [sum_{k=0}^nA_kB_{i+k}(A_k-B_{i+k})^2 ]

    拆开得到:

    [sum_{k=0}^n(A_k^3B_{i+k}-2A_k^2B_{i+k}^2+A_kB_{i+k}^3) ]

    考虑设(T_{k}=A_{n-k}),即(A)串的倒串,则原式等同于:

    [sum_{k=0}^n(T_{n-k}^3B_{i+k}-2T_{n-k}^2B_{i+k}^2+T_{n-k}B_{i+k}^3) ]

    然后我们就能发现一件很神奇的是:每对相乘的项下标之和都是(n+i),在(i)一定时是个定值!

    所以,我们可以把上面的式子改成卷积形式,即:

    [sum_{x+y=n+i}(T_{x}^3B_{y}-2T_{x}^2B_{y}^2+T_{x}B_{y}^3) ]

    这么一来,我们只要对(T,T^2,T^3,B,B^2,B^3)六个多项式分别做(DFT),进行上述运算后,再做(IDFT),即可得到一个新的多项式(P)

    而从(B)中第(i)位开始的连续(n)个字符能与(A)完全匹配,当且仅当(P)中第(n+i)项的系数为(0)

    这样这题就做完了。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 300000
    #define LL long long
    #define DB double
    using namespace std;
    int n,m,a[4*N+5],b[4*N+5];char s[N+5];
    template<int SZ> class Poly
    {
    	private:
    		int P,L,R[4*N+5];DB Pi;
    		struct node
    		{
    			DB x,y;I node(Con DB& a=0,Con DB& b=0):x(a),y(b){}
    			I node operator + (Con node& o) Con {return node(x+o.x,y+o.y);}
    			I node operator - (Con node& o) Con {return node(x-o.x,y-o.y);}
    			I node operator * (Con node& o) Con {return node(x*o.x-y*o.y,x*o.y+y*o.x);}
    		}A[4*N+5],A2[4*N+5],A3[4*N+5],B[4*N+5],B2[4*N+5],B3[4*N+5];
    		I void T(node *s,CI op)
    		{
    			RI i,j,k;node U,S,x,y;for(i=0;i^P;++i) i<R[i]&&(U=s[i],s[i]=s[R[i]],s[R[i]]=U,0);
    			for(i=1;i^P;i<<=1) for(U=node(cos(Pi/i),op*sin(Pi/i)),j=0;j^P;j+=i<<1)
    				for(S=node(1,0),k=0;k^i;++k,S=S*U) s[j+k]=(x=s[j+k])+(y=S*s[i+j+k]),s[i+j+k]=x-y;
    		}
    	public:
    		I Poly() {Pi=acos(-1);}
    		Tp I void FFT(CI n,CI m,Ty *a,Ty *b)
    		{
    			RI i;P=1,L=0;W(P<=n+m) P<<=1,++L;for(i=0;i^P;++i) R[i]=(R[i>>1]>>1)|((i&1)<<L-1);
    			for(i=0;i<=n;++i) A[i]=a[i],A2[i]=A[i]*A[i],A3[i]=A[i]*A2[i];//记录多项式
    			for(i=0;i<=m;++i) B[i]=b[i],B2[i]=B[i]*B[i],B3[i]=B[i]*B2[i];//记录多项式
    			T(A,1),T(A2,1),T(A3,1),T(B,1),T(B2,1),T(B3,1);//DFT
    			for(i=0;i^P;++i) A[i]=A3[i]*B[i]-node(2)*A2[i]*B2[i]+A[i]*B3[i];//运算
    			for(T(A,-1),i=0;i<=m;++i) a[i]=A[i].x/P+0.5;//IDFT
    		}
    };Poly<N> P;
    int main()
    
    	RI i,t=0;scanf("%d%d",&n,&m),--n,--m;
    	for(scanf("%s",s),i=0;i<=n;++i) a[n-i]=s[i]=='*'?0:(s[i]&31);//注意A串取倒串
    	for(scanf("%s",s),i=0;i<=m;++i) b[i]=s[i]=='*'?0:(s[i]&31);
    	for(P.FFT(n,m,a,b),i=0;i<=m-n;++i) t+=!a[n+i];printf("%d
    ",t);//统计个数
    	for(i=0;i<=m-n;++i) !a[n+i]&&printf("%d ",i+1);return 0;//输出合法位置
    }
    
  • 相关阅读:
    Django模板系统
    __new__与__init__的区别
    django中models field详解
    快速入门mysql
    jq2
    jq
    前端 js 部分
    前端 js
    前端 css
    前端 table form
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu4173.html
Copyright © 2020-2023  润新知