zoukankan      html  css  js  c++  java
  • bzoj 4503 两个串 快速傅里叶变换FFT

    题目大意:

    给定两个((length leq 10^5))的字符串,问第二个串在第一个串中出现了多少次。并且第二个串中含有单字符通配符.

    题解:

    首先我们从kmp的角度去考虑
    这道题从字符串数据结构的方面去考虑你就错了
    因为这是我在刷FFT时遇到的题嘛,,直接就去想FFT的做法
    我们知道,两个字符串相等当且仅当

    [(sum_{i=1}^n(s1[i] - s2[i])^2*[s2[i] eq ?]) = 0 ]

    注:可以将s1理解为从第一个串中取出的一个后缀
    那么我们对每个s1中的下标就套用公式计算一下即可
    我们肯定不能直接计算...
    (f(x) = sum_{i=1}^n(s1[o] - s2[i])^2*[s2[i] eq ?]))
    我们有

    [f(x) = sum_{i=1}^n(s1[i] - s2[i])^2*[s2[i] eq ?]) ]

    [f(x) = sum_{i=1}^ns1[i]^2*s2[i] - sum{i=1}^n2*s1[i]*s2[i]^2+sum_{i=1}^ns2[i]^3 ]

    针对最后一项求和我们直接预处理即可
    有些麻烦的就是前面的两项
    但是我们有FFT啊,FFT的作用是计算卷积,可是这明明不是卷积啊?
    转化成卷积不就行了。。
    我们反转s2这个字符串
    然后我们发现,此时有

    [f(x) = sum_{i=1}^ns1[i]^2*s2[n-i+1] - sum{i=1}^n2*s1[j]*s2[n-1+1]^2+sum_{i=1}^ns2[i]^3 ]

    前两项变成了标准的卷积形式,所以我们用FFT加速可以在(nlogn)内计算
    总时间复杂度(O(nlogn))

    #include <cmath>
    #include <cstdio>	
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    inline void read(int &x){
    	x=0;char ch;bool flag = false;
    	while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
    	while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
    }
    inline int cat_max(const int &a,const int &b){return a>b ? a:b;}
    inline int cat_min(const int &a,const int &b){return a<b ? a:b;}
    const int maxn = 210010;
    const double pi = acos(-1);
    const double eps = 1e-8;
    struct complex{
    	double x,y;
    	complex(){}
    	complex(double a,double b){x=a;y=b;}
    	complex operator + (complex rhs){return complex(x+rhs.x,y+rhs.y);}
    	complex operator - (complex rhs){return complex(x-rhs.x,y-rhs.y);}
    	complex operator * (complex rhs){return complex(x*rhs.x-y*rhs.y,x*rhs.y+y*rhs.x);}
    	complex operator / (double rhs){return complex(x/rhs,y/rhs);}
    };
    complex a[maxn<<1],b[maxn<<1],c[maxn<<1];
    void FFT(complex *x,int n,int p){
    	for(int i=0,t=0;i<n;++i){
    		if(i > t) swap(x[i],x[t]);
    		for(int j = n>>1;(t^=j) < j;j >>= 1);
    	}
    	for(int m=2;m<=n;m<<=1){
    		complex wn(cos(p*2*pi/m),sin(p*2*pi/m));
    		for(int i=0;i<n;i+=m){
    			complex w(1,0),u;
    			int k = m>>1;
    			for(int j=0;j<k;++j,w=w*wn){
    				u = x[i+j+k]*w;
    				x[i+j+k] = x[i+j] - u;
    				x[i+j] = x[i+j] + u;
    			}
    		}
    	}
    	if(p == -1) for(int i=0;i<n;++i) x[i] = x[i]/n;
    }
    char tmp[maxn];
    int s1[maxn],s2[maxn];
    int main(){
    	scanf("%s",tmp);int n = strlen(tmp);
    	for(int i=0;i<n;++i) s1[i] = tmp[i] - 'a' + 1;
    	scanf("%s",tmp);int m = strlen(tmp);
    	double sum = 0.0;
    	for(int i=0;i<m;++i){
    		s2[m-i-1] = tmp[i] == '?' ? 0 : tmp[i] - 'a' + 1;
    		sum += s2[m-i-1]*s2[m-i-1]*s2[m-i-1];
    	}
    
    	int len = 0;
    	for(int i=1;(i>>2)<n;i<<=1) len = i;
    	for(int i=0;i<n;++i) a[i] = complex(s1[i]*s1[i],0);
    	for(int i=0;i<m;++i) b[i] = complex(s2[i],0);
    	FFT(a,len,1);FFT(b,len,1);
    	for(int i=0;i<len;++i) c[i] = a[i]*b[i];
    	memset(a,0,sizeof a);memset(b,0,sizeof b);
    	for(int i=0;i<n;++i) a[i] = complex(s1[i],0);
    	for(int i=0;i<m;++i) b[i] = complex(s2[i]*s2[i],0);
    	FFT(a,len,1);FFT(b,len,1);
    	for(int i=0;i<len;++i) c[i] = c[i] - (a[i]*b[i]) - (a[i]*b[i]);
    	FFT(c,len,-1);
    	int ans = 0;
    	for(int i=m-1;i<n;++i)
    		if( (int)(c[i].x + sum + 0.5) == 0 ) ++ans;
    	printf("%d
    ",ans);
    	for(int i=m-1;i<n;++i)
    		if( (int)(c[i].x + sum + 0.5) == 0 ) printf("%d
    ",i-m+1);
    	getchar();getchar();
    	return 0;
    }
    
    
  • 相关阅读:
    Rational全系列工具介绍
    转贴 MM(ModelMaker)建模工具快速上手指南delphi
    eclipse打不开报(Failed to create the Java Virtual Machine)解决方法
    Vagrant系列(二)Vagrant的配置文件Vagrantfile详解
    Xshell登录Vagrant方式
    win10系统在执行“ vagrant box add centos7 vagrantcentos7.box”添加box时,报错“Vagrant failed to initialize at a...
    win10系统搭建vagrant时开启bios,虚拟化问题
    查看memcache版本
    python空为None
    python 获得字符串长度
  • 原文地址:https://www.cnblogs.com/Skyminer/p/6353816.html
Copyright © 2011-2022 走看看