利用FFT来进行字符串匹配

给定串A和串B,A由26个小写字母构成,B由?和26个小写字母构成

?可以和任意字符匹配

求A中出现了多少次B

 

这里可以使用fft做法,定义向量A和向量B

然后求A和rev(B)的卷积结果C

C的第i-len(B)位就可以表示匹配结果

如果C的第i-len(B)位恰好是B中除了?的字符个数,那么就是匹配成功

这样复杂度就是O((n+m)*(logn + logm))

 

注意要调整eps,当数据很大的时候,误差会比较大

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <complex>
using namespace std;
const double pi = acos(-1);
const int maxn =  111111;
typedef complex<double> Complex;
const double eps = 1e-8;
void DFT(Complex *a, int n, int t)
{
    if(n == 1) return;
    Complex a0[n>>1], a1[n>>1];
    for(int i = 0; i < n; i += 2) a0[i>>1] = a[i], a1[i>>1] = a[i+1];
    DFT(a0, n>>1, t); DFT(a1, n>>1, t);
    Complex wn(cos(2*pi/n), t*sin(2*pi/n)), w(1, 0);
    for(int i = 0; i < (n>>1); i++, w *= wn) a[i] = a0[i] + w*a1[i], a[i+(n>>1)] = a0[i] - w*a1[i];
}
Complex a[maxn], b[maxn];
int n1, n2, nn, c[maxn];
double x;
string s1, s2;
int main()
{
    freopen("a.txt", "r", stdin);
    cin>>s1>>s2;
    n1 = s1.length(); n2 = s2.length();
    int N = n2;
    for(int i = 0; i < n1; i++) x = 2*pi*(s1[i] - 'a')/26, a[i] = Complex(cos(x), sin(x));
    for(int i = 0; i < n2; i++)
        if(s2[i] != '?') x = 2*pi*(s2[i] - 'a')/26, b[i] = Complex(cos(-x), sin(-x));
        else b[i] = Complex(0, 0), N--;
    for(int i = 0; i < n2/2; i++) swap(b[i], b[n2-i-1]);
    n1--; n2--;
    nn = 1; while(nn <= n1+n2) nn <<= 1;
    DFT(a, nn, 1); DFT(b, nn, 1);
    for(int i = 0; i <= nn; i++) a[i] = a[i]*b[i];
    DFT(a, nn, -1);
    for(int i = 0; i <= n1+n2; i++) c[i] = abs(a[i].imag()) < eps ? (a[i].real()/nn + eps) : 0;
    int ans = 0;
    for(int i = n2; i <= n1; i++) if(c[i] == N) ans++;
    cout<<ans<<endl;
    return 0;
}

 

posted @ 2017-01-25 14:12  Saurus  阅读(1845)  评论(0编辑  收藏  举报