zoukankan      html  css  js  c++  java
  • BZOJ4259

    Portal

    Description

    给出一个长度为(m(mleq3 imes10^5))的字符串(A),一个长度为(n(mleq3 imes10^5))的字符串(B),其中仅包含小写字母(a)~(z)和通配符(*)。求对于每个(iin[0,n-m])(B_{i..i+m-1})是否能与(A_{0..m-1})匹配。

    Solution

    没想到这居然是个FFT!
    定义(d(x)=sum_{i=0}^{m-1} A_i B_{x+i}(A_i-B_{x+i})^2),则(d(x)=0 Leftrightarrow B_{x..x+m-1})(A_{0..m-1})匹配。将(A)翻转为(A'),令(f(x)=d(x-m+1)),则有:

    [egin{align*} f(x)=d(x-m) &= sum_{i=0}^{m-1} A'_{m-i-1} B_{x-m+1+i}(A'_{m-i}-B_{x-m+i})^2 \ f(x) &= sum_{i=0}^{m-1}A'_iB_{x-i}(A'_i-B_{x-i})^2 \ f(x) &= sum_{i=0}^{m-1}A'^3_iB_{x-i} - 2sum_{i=0}^{m-1}A'^2_iB^2_{x-i} + sum_{i=0}^{m-1}A'_iB^3_{x-i} end{align*}$$用FFT计算所有的$f(x)$即可。注意频域加减等价于时域加减,所以只用做7遍FFT。 > 时间复杂度$O(7nlogn)。$ ##Code **`std::complex`太慢!要是想过这个题得手写`complex`** ```cpp //残缺的字符串 #include <algorithm> #include <complex> #include <cstdio> using std::swap; typedef std::complex<double> cpx; int const N=(1<<20)+10; double const PI=acos(-1); double const EPS=1e-5; int n,m,a0[N],b0[N]; char s[N]; int t,pos[N]; cpx a[N],b[N],c[N]; void write(cpx x) {printf("(%.2lf %.2lf) ",x.real(),x.imag());} bool equal0(cpx x) {return (int)(x.real()+0.5)==0;} void FFT(cpx x[],int f) { for(int i=0;i<t;i++) if(i<pos[i]) swap(x[i],x[pos[i]]); for(int i=1;i<t;i<<=1) { cpx Wn=cpx(cos(PI/i),f*sin(PI/i)); for(int j=0;j<t;j+=i+i) { cpx w=1; for(int k=0;k<i;k++,w=w*Wn) { cpx p=x[j+k],q=w*x[j+k+i]; x[j+k]=p+q,x[j+k+i]=p-q; } } } if(f==-1) for(int i=0;i<t;i++) x[i]/=t; } void convo(int k1,int k2) { for(int i=0;i<t;i++) a[i]=pow(a0[i],k1); for(int i=0;i<t;i++) b[i]=pow(b0[i],k2); FFT(a,1),FFT(b,1); for(int i=0;i<t;i++) c[i]=a[i]*b[i]; } cpx f[N]; int cnt,ans[N]; int main() { scanf("%d%d",&m,&n); scanf("%s",s); for(int i=0;i<m;i++) a0[m-i-1]=s[i]^'*'?s[i]:0; scanf("%s",s); for(int i=0;i<n;i++) b0[i]=s[i]^'*'?s[i]:0; t=1; int n0=0; while(t<=n+m) t<<=1,n0++; for(int i=0;i<t;i++) pos[i]=(pos[i>>1]>>1)|((i&1)<<(n0-1)); convo(3,1); for(int i=0;i<t;i++) f[i]+=c[i]; convo(2,2); for(int i=0;i<t;i++) f[i]-=c[i]+c[i]; convo(1,3); for(int i=0;i<t;i++) f[i]+=c[i]; FFT(f,-1); int cnt=0; for(int i=m-1;i<n;i++) if(equal0(f[i])) ans[++cnt]=i-m+2; printf("%d ",cnt); if(!cnt) return 0; for(int i=1;i<=cnt;i++) printf("%d ",ans[i]); puts(""); return 0; } ``` ##P.S. 其实复杂度是$7 imes2^{20} imes20 approx 1.4 imes10^8$,本来就会T的。 不想手写`complex`,弃了弃了...]

  • 相关阅读:
    jQuery轮播图(一)轮播实现并封装
    openSUSE 12.3 默认启动项
    最大堆(最小堆)
    二叉树基本操作续二:前序、中序、后序遍历(非递归 迭代方式)
    二叉树基本操作续一:二叉树建立、节点数统计
    二叉树基本操作:前序、中序、后序遍历(递归方式)
    Android如何打印std::cout/printf(重定向stdout)
    textarea高度跟随文字高度而变化
    箭头函数与普通函数的区别
    浏览器兼容问题
  • 原文地址:https://www.cnblogs.com/VisJiao/p/BZOJ4259.html
Copyright © 2011-2022 走看看