FFT题集

FFT学习参考这两篇博客,很详细,结合这看,互补。

博客一

博客二

很大一部分题目需要构造多项式相乘来进行计数问题。

1. HDU 1402 A * B Problem Plus

 把A和B分别当作多项式的系数。

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;

const double PI = acos(-1.0);
const int maxn = 5e4+50;
struct Complex
{
    double real,image; ///实部和虚部
    Complex(double _real,double _image)
    {
        real = _real;
        image = _image;
    }
    Complex(){}
};

Complex operator + (const Complex &c1,const Complex &c2)
{
    return Complex(c1.real+c2.real,c1.image+c2.image);
}

Complex operator - (const Complex &c1,const Complex &c2)
{
    return Complex(c1.real-c2.real,c1.image-c2.image);
}

Complex operator * (const Complex &c1,const Complex &c2)
{
    return Complex(c1.real*c2.real-c1.image*c2.image, c1.real*c2.image + c1.image * c2.real);
}

int rev(int id,int len)
{
    int ret = 0;
    for(int i=0;(1<<i)<len;i++)
    {
        ret <<= 1;
        if(id & (1<<i)) ret |= 1;
    }
    return ret;
}
Complex A[135000];
void FFT(Complex* a,int len,int DFT) ///对a进行DFT或者逆DFT,结果存在a当中
{
    for(int i = 0; i < len; i++)
        A[rev(i,len)] = a[i]; ///按其在叶子节点中的顺序存储
    for(int s = 1; (1 << s)<= len; s++)
    {
        int m = (1 << s);
        Complex wm = Complex(cos(DFT*2*PI/m),sin(DFT*2*PI/m)); ///主n次单位根
        for(int k = 0; k < len; k += m)
        {
            Complex w = Complex(1, 0); ///旋转因子
            for(int j = 0; j < (m >> 1); j++)
            {
                Complex t = w * A[k + j + (m >> 1)];
                Complex u = A[k + j];
                A[k + j] = u + t;
                A[k + j + (m >> 1)] = u - t;
                w = w * wm;
            }
        }
    }
    if(DFT == -1) for(int i = 0; i < len; i++) A[i].real /= len, A[i].image /= len;
    for(int i = 0; i < len; i++) a[i] = A[i];
    return;
}
char coA[maxn],coB[maxn];
///把每一位作为系数

///乘积后次数最大为2 * n - 2,转换成2的k次幂,(1<<16)<2*n-2<(1<<17)
Complex a[135000],b[135000];
int ans[135000];

int main()
{
    while(scanf("%s",coA)!=EOF)
    {
        int lenA = strlen(coA);
        int mia = 0;
        while((1<<mia)<lenA) mia++; ///2^mia>=lenA
        scanf("%s",coB);
        int lenB = strlen(coB);
        int mib = 0;
        while((1<<mib)<lenB) mib++;
        int len(1<<(max(mia,mib)+1));
        for(int i=0;i<len;i++)
        {
            if(i<lenA) a[i] = Complex(coA[lenA-i-1]-'0',0); ///表示系数,A数组从左往右存了进去 2000 --> 0002
            else a[i] = Complex(0,0);
            if(i<lenB) b[i] = Complex(coB[lenB-i-1]-'0',0);
            else b[i] = Complex(0,0);
        }
        ///求A和B的点值表达式
        FFT(a, len, 1);
        FFT(b, len, 1);
        for(int i = 0; i < len; i++)
            a[i] = a[i] * b[i]; ///求C的点值
        FFT(a, len, -1); ///逆DFT得到系数
        for(int i = 0; i < len; i++)
            ans[i] = (int)(a[i].real + 0.5); ///四舍五入
        for(int i = 0; i <len - 1; i++)
        {
            ans[i + 1] += ans[i] / 10;
            ans[i] %= 10;
        }
        bool flag = 0;
        for(int i = len - 1; i >= 0; i--) ///防止出现前导0
        {
            if(ans[i]) printf("%d",ans[i]), flag = 1;
            else if(flag || i == 0) printf("0");
        }
        puts("");
    }
    return 0;
}
Code

 2.Golf Bot

从K中选两个或者1个,选两个相当于指数相加,比如x^5与x^1相乘,相当于5和1都被选了,直接相加,所以把k中的数字当成系数1,没出现过当成系数0。 

对于选1个的可以把x^0的系数设为1.

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <map>
using namespace std;

const double PI = acos(-1.0);
const int maxn = 2e5+50;
struct Complex
{
    double real,image; ///实部和虚部
    Complex(double _real,double _image)
    {
        real = _real;
        image = _image;
    }
    Complex(){}
};

Complex operator + (const Complex &c1,const Complex &c2)
{
    return Complex(c1.real+c2.real,c1.image+c2.image);
}

Complex operator - (const Complex &c1,const Complex &c2)
{
    return Complex(c1.real-c2.real,c1.image-c2.image);
}

Complex operator * (const Complex &c1,const Complex &c2)
{
    return Complex(c1.real*c2.real-c1.image*c2.image, c1.real*c2.image + c1.image * c2.real);
}

int rev(int id,int len)
{
    int ret = 0;
    for(int i=0;(1<<i)<len;i++)
    {
        ret <<= 1;
        if(id & (1<<i)) ret |= 1;
    }
    return ret;
}
Complex A[540000];
void FFT(Complex* a,int len,int DFT) ///对a进行DFT或者逆DFT,结果存在a当中
{
    for(int i = 0; i < len; i++)
        A[rev(i,len)] = a[i]; ///按其在叶子节点中的顺序存储
    for(int s = 1; (1 << s)<= len; s++)
    {
        int m = (1 << s);
        Complex wm = Complex(cos(DFT*2*PI/m),sin(DFT*2*PI/m)); ///主n次单位根
        for(int k = 0; k < len; k += m)
        {
            Complex w = Complex(1, 0); ///旋转因子
            for(int j = 0; j < (m >> 1); j++)
            {
                Complex t = w * A[k + j + (m >> 1)];
                Complex u = A[k + j];
                A[k + j] = u + t;
                A[k + j + (m >> 1)] = u - t;
                w = w * wm;
            }
        }
    }
    if(DFT == -1) for(int i = 0; i < len; i++) A[i].real /= len, A[i].image /= len;
    for(int i = 0; i < len; i++) a[i] = A[i];
    return;
}
int coA[maxn];
///把每一位作为系数
///乘积后次数最大为2 * n - 2,转换成2的k次幂,(1<<16)<2*n-2<(1<<17)
Complex a[540000];
int ans[540000];
int num[maxn];
int main()
{
  //  freopen("1.txt","r",stdin);
    int n,m;
    while(scanf("%d", &n)!=EOF)
    {
        memset(coA, 0, sizeof(coA));
        int maxv = 0;
        for(int i = 0; i < n; i++)
        {
            int x;
            scanf("%d", &x);
            coA[x] = 1;
            maxv = max(maxv, x);
        }
        coA[0] = 1;
        int mia = 0;
        while((1 << mia) < maxv) mia++;
        int len = (1 << (mia + 1));
        for(int i = 0; i < len; i++)
        {
            if(i <= maxv && coA[i]) a[i] = Complex(1,0);
            else a[i] = Complex(0, 0);
        }
        ///求A和B的点值表达式
        FFT(a, len, 1);
        for(int i = 0; i < len; i++)
            a[i] = a[i] * a[i]; ///求C的点值
        FFT(a, len, -1); ///逆DFT得到系数
        memset(ans,0,sizeof(ans));
        for(int i = 0; i < len; i++)
            ans[i] = (int)(a[i].real + 0.5); ///四舍五入
        int cnt = 0;
        scanf("%d", &m);
        for(int i = 0; i < m; i++)
        {
            int x;
            scanf("%d", &x);
            if(ans[x])  cnt++;
        }
        printf("%d
",cnt);
    }
    return 0;
}
Code

3.HDU - 4609 3-idiots

之前做过给定n个数,任意两个数求和,求总共有多少个不同的,每个数出现过它的系数标记为1,这样FFT后,某个系数不等于0就代表这个数出现过。

这个题,算两个边求和,然后枚举另一条边,算能构成三角形的次数。

以下以1 3 3 4为例。

把长度归为指数后,次数归为系数后,指数从大到小变成{1,2,0,1,0},自己卷积自己{1,2,0,1,0} * {1,2,0,1,0}

得到{1,4,4,2,4,0,1,0,0},代表每一个和出现的次数,用ans数组表示

因为构成三角形,不能两次取同一根棍,所以减去两次用同一根棍的

for(int i = 0; i < n; i++) ans[a[i] + a[i]]--;

还有因为卷积的时候,1 4 和 4 1有了顺序,我们在选三角形的时候是没有顺序的,所以要/2

for(int i = 0; i <= len; i++) ans[i] /= 2;

为了后面统计方便,我们求个ans的前缀和,得到sum数组。

我们对所有边排个序,然后枚举一条边a[i],我们假设它是所选三角形里面长度最大的,那么我们能够成三角形的条件之一是两边之和 > a[i],所以 cnt[i] += sum[len] - sum[a[i]].

但是这里面的和有不符合条件的:

1.一大一小,cnt -= (n - i - 1) * i;

2.一个是它本身,另一个是其他,cnt -= (n-1);

3.两个都是大于的,C(n - i - 1, 2)

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <map>
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const int maxn = (1 << 18) + 50;
struct Complex
{
    double real,image; ///实部和虚部
    Complex(double _real,double _image)
    {
        real = _real;
        image = _image;
    }
    Complex(){}
};

Complex operator + (const Complex &c1,const Complex &c2)
{
    return Complex(c1.real+c2.real,c1.image+c2.image);
}

Complex operator - (const Complex &c1,const Complex &c2)
{
    return Complex(c1.real-c2.real,c1.image-c2.image);
}

Complex operator * (const Complex &c1,const Complex &c2)
{
    return Complex(c1.real*c2.real-c1.image*c2.image, c1.real*c2.image + c1.image * c2.real);
}

int rev(int id,int len)
{
    int ret = 0;
    for(int i=0;(1<<i)<len;i++)
    {
        ret <<= 1;
        if(id & (1<<i)) ret |= 1;
    }
    return ret;
}
Complex A[maxn];
void FFT(Complex* a,int len,int DFT) ///对a进行DFT或者逆DFT,结果存在a当中
{
    for(int i = 0; i < len; i++)
        A[rev(i,len)] = a[i]; ///按其在叶子节点中的顺序存储
    for(int s = 1; (1 << s)<= len; s++)
    {
        int m = (1 << s);
        Complex wm = Complex(cos(DFT*2*PI/m),sin(DFT*2*PI/m)); ///主n次单位根
        for(int k = 0; k < len; k += m)
        {
            Complex w = Complex(1, 0); ///旋转因子
            for(int j = 0; j < (m >> 1); j++)
            {
                Complex t = w * A[k + j + (m >> 1)];
                Complex u = A[k + j];
                A[k + j] = u + t;
                A[k + j + (m >> 1)] = u - t;
                w = w * wm;
            }
        }
    }
    if(DFT == -1) for(int i = 0; i < len; i++) A[i].real /= len, A[i].image /= len;
    for(int i = 0; i < len; i++) a[i] = A[i];
    return;
}
int coA[maxn];
///把每一位作为系数
///乘积后次数最大为2 * n - 2,转换成2的k次幂,(1<<17)<2*n-2<(1<<18)
Complex a[maxn];
ll sum[maxn];
ll ans[maxn];
int bran[maxn];
int main()
{
  //  freopen("1.txt","r",stdin);
    int T; scanf("%d", &T);
    int n;
    while(T--)
    {
        scanf("%d", &n);
        memset(coA, 0, sizeof(coA));
        int maxv = 0;
        for(int i = 0; i < n; i++)
        {
            int x;
            scanf("%d", &x);
            bran[i] = x;
            coA[x]++;
        }
        sort(bran, bran + n);
        int mia = 0;
        while((1 << mia) < (bran[n - 1] + 1) ) mia++;
        int len = (1 << (mia + 1));
        for(int i = 0; i < len; i++)
        {
            if(coA[i]) a[i] = Complex(coA[i], 0);
            else a[i] = Complex(0, 0);
        }
        ///求A和B的点值表达式
        FFT(a, len, 1);
        for(int i = 0; i < len; i++)
            a[i] = a[i] * a[i]; ///求C的点值
        FFT(a, len, -1); ///逆DFT得到系数
        memset(sum, 0, sizeof(sum));
        for(int i = 0; i < len; i++)
            ans[i] = (ll)(a[i].real + 0.5); ///四舍五入
        for(int i = 0; i < n; i++) ans[bran[i] + bran[i]]--;
        for(int i = 1; i < len; i++)
        {
            ans[i] /= 2LL;
            sum[i] = sum[i - 1] + ans[i];
        }
        ll cnt = 0;
        for(int i = 0; i < n; i++)
        {
            cnt += sum[len - 1] - sum[bran[i]];
            cnt -= (ll)(n - i - 1) * i;
            cnt -= (ll)(n - 1);
            cnt -= (ll)(n - i - 1) * (n - i - 2) / 2;
        }
        ll all = (ll)n * (n - 1) * (n - 2) / 6LL;
        printf("%.7f
", (double)cnt / all);
    }
    return 0;
}
Code

 4. K-neighbor substrings

字符串的这一类型变换最近已经碰到四道了,以这道题为例,学习这一类算法。

一开始看题解,好多人都说,要把B串翻转,我一直在想为什么翻转

看完这两个文章后,

https://blog.csdn.net/q582116859/article/details/77248970

https://www.zhihu.com/question/22298352

我发现我一直纠结与卷积代码的实现,而忽略了卷积数学公式的书写。

以知乎中掷两颗色子,和为4为例。

看完这个例子就明白了为啥要逆序,因为B串是逆序比较的。

最后得到的卷子公式

4 - m + m = 4,代表两个因变量之和是4,

到我们这道题,就是

这个上面有点问题,X是从0~n-1,Y是0~m-1.

最后以i开头的字符比较,Hamming距离都累加到 i + m - 1上了。

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <map>
#include <set>
using namespace std;
typedef unsigned long long ll;
const double PI = acos(-1.0);
const int maxn = (1 << 18) + 50;
struct Complex
{
    double real,image; ///实部和虚部
    Complex(double _real,double _image)
    {
        real = _real;
        image = _image;
    }
    Complex(){}
};

Complex operator + (const Complex &c1,const Complex &c2)
{
    return Complex(c1.real+c2.real,c1.image+c2.image);
}

Complex operator - (const Complex &c1,const Complex &c2)
{
    return Complex(c1.real-c2.real,c1.image-c2.image);
}

Complex operator * (const Complex &c1,const Complex &c2)
{
    return Complex(c1.real*c2.real-c1.image*c2.image, c1.real*c2.image + c1.image * c2.real);
}

int rev(int id,int len)
{
    int ret = 0;
    for(int i=0;(1<<i)<len;i++)
    {
        ret <<= 1;
        if(id & (1<<i)) ret |= 1;
    }
    return ret;
}
Complex A[maxn];
void DFT(Complex* a,int len,int flag) ///对a进行DFT或者逆DFT,结果存在a当中
{
    for(int i = 0; i < len; i++)
        A[rev(i,len)] = a[i]; ///按其在叶子节点中的顺序存储
    for(int s = 1; (1 << s)<= len; s++)
    {
        int m = (1 << s);
        Complex wm = Complex(cos(flag*2*PI/m),sin(flag*2*PI/m)); ///主n次单位根
        for(int k = 0; k < len; k += m)
        {
            Complex w = Complex(1, 0); ///旋转因子
            for(int j = 0; j < (m >> 1); j++)
            {
                Complex t = w * A[k + j + (m >> 1)];
                Complex u = A[k + j];
                A[k + j] = u + t;
                A[k + j + (m >> 1)] = u - t;
                w = w * wm;
            }
        }
    }
    if(flag == -1) for(int i = 0; i < len; i++) A[i].real /= len, A[i].image /= len;
    for(int i = 0; i < len; i++) a[i] = A[i];
    return;
}
///把每一位作为系数
///乘积后次数最大为2 * n - 2,转换成2的k次幂,(1<<17)<2*n-2<(1<<18)
Complex a[maxn];
Complex b[maxn];
int num[maxn];
void FFT(int len)
{
    DFT(a, len, 1);
    DFT(b, len, 1);
    for(int i = 0; i < len; i++)
        a[i] = a[i] * b[i];
    DFT(a, len, -1);
    for(int i = 0; i < len; i++)
        num[i] += (int)(a[i].real + 0.5);
}
int coA[maxn];
char s1[maxn];
char s2[maxn];
#define pow Pow
ll pow[maxn];
ll Hash[maxn];
ll magic = 3;
///hash利用自然数溢出来取模
set<ll> st;
int main()
{
    int K, kase = 0;
    pow[0] = 1;
    for(int i = 1; i < maxn; i++) pow[i] = pow[i - 1] * magic;
    while(scanf("%d", &K) && K != -1)
    {
        scanf("%s %s", s1, s2);
        int la = strlen(s1);
        int lb = strlen(s2);
        printf("Case %d: ", ++kase);
        if(la < lb)
        {
            puts("0");
            continue;
        }
        int mi = 0;
        while((1 << mi) < (max(la, lb) + 1)) mi++;
        int len = (1 << (mi + 1));
        for(int i = 0; i < lb / 2; i++) swap(s2[i], s2[lb - i - 1]);
        ///A串的a取1,B串的b取1
        memset(num, 0, sizeof(num));
        for(int i = 0; i < len; i++)
        {
            if(i < la) a[i] = Complex((s1[i] == 'a' ? 1 : 0), 0);
            else a[i] = Complex(0, 0);
            if(i < lb) b[i] = Complex((s2[i] == 'b' ? 1 : 0), 0);
            else b[i] = Complex(0, 0);
        }
        FFT(len);
        for(int i = 0; i < len; i++)
        {
       //     printf("%d ", num[i]);
        }
        for(int i = 0; i < len; i++)
        {
            if(i < la) a[i] = Complex((s1[i] == 'b' ? 1 : 0), 0);
            else a[i] = Complex(0, 0);
            if(i < lb) b[i] = Complex((s2[i] == 'a' ? 1 : 0), 0);
            else b[i] = Complex(0, 0);
        }
        FFT(len);
        Hash[0] = 0;
        for(int i = 0; i < la; i++) Hash[i + 1] = Hash[i] * magic + (ll)(s1[i] - 'a' + 1);
        int cnt = 0;
        st.clear();
        for(int i = lb - 1; i < la; i++)
        {
          //  printf("%d ",num[i]);
            ll now = Hash[i + 1] - Hash[i - lb + 1] * pow[lb];
            if(st.count(now)) continue;
            if(num[i] <= K)
            {
                st.insert(now);
                cnt++;
            }
        }
        printf("%d
", cnt);
    }
    return 0;
}

Code
Code

5. Rock Paper Scissors Lizard Spock.

会了上面的字符串的题,这种类型的就差不多会了。

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <map>
#include <set>
using namespace std;
typedef unsigned long long ll;
const double PI = acos(-1.0);
const int maxn = (1 << 21) + 50;
struct Complex
{
    double real,image; ///实部和虚部
    Complex(double _real,double _image)
    {
        real = _real;
        image = _image;
    }
    Complex(){}
};

Complex operator + (const Complex &c1,const Complex &c2)
{
    return Complex(c1.real+c2.real,c1.image+c2.image);
}

Complex operator - (const Complex &c1,const Complex &c2)
{
    return Complex(c1.real-c2.real,c1.image-c2.image);
}

Complex operator * (const Complex &c1,const Complex &c2)
{
    return Complex(c1.real*c2.real-c1.image*c2.image, c1.real*c2.image + c1.image * c2.real);
}

int rev(int id,int len)
{
    int ret = 0;
    for(int i=0;(1<<i)<len;i++)
    {
        ret <<= 1;
        if(id & (1<<i)) ret |= 1;
    }
    return ret;
}
Complex A[maxn];
void DFT(Complex* a,int len,int flag) ///对a进行DFT或者逆DFT,结果存在a当中
{
    for(int i = 0; i < len; i++)
        A[rev(i,len)] = a[i]; ///按其在叶子节点中的顺序存储
    for(int s = 1; (1 << s)<= len; s++)
    {
        int m = (1 << s);
        Complex wm = Complex(cos(flag*2*PI/m),sin(flag*2*PI/m)); ///主n次单位根
        for(int k = 0; k < len; k += m)
        {
            Complex w = Complex(1, 0); ///旋转因子
            for(int j = 0; j < (m >> 1); j++)
            {
                Complex t = w * A[k + j + (m >> 1)];
                Complex u = A[k + j];
                A[k + j] = u + t;
                A[k + j + (m >> 1)] = u - t;
                w = w * wm;
            }
        }
    }
    if(flag == -1) for(int i = 0; i < len; i++) A[i].real /= len, A[i].image /= len;
    for(int i = 0; i < len; i++) a[i] = A[i];
    return;
}
///把每一位作为系数
///乘积后次数最大为2 * n - 2,转换成2的k次幂,(1<<17)<2*n-2<(1<<18)
Complex a[maxn];
Complex b[maxn];
int num[maxn];
void FFT(int len)
{
    DFT(a, len, 1);
    DFT(b, len, 1);
    for(int i = 0; i < len; i++)
        a[i] = a[i] * b[i];
    DFT(a, len, -1);
    for(int i = 0; i < len; i++)
        num[i] += (int)(a[i].real + 0.5);
}
char s1[maxn];
char s2[maxn];
int la, lb, len;
void calc(char win, char lose)
{
    for(int i = 0; i < len; i++)
    {
        if(i < la) a[i] = Complex((s1[i] == lose ? 1 : 0), 0);
        else a[i] = Complex(0, 0);
        if(i < lb) b[i] = Complex((s2[i] == win ? 1 : 0), 0);
        else b[i] = Complex(0, 0);
    }
    FFT(len);
}
int main()
{
    scanf("%s %s", s1, s2);
    la = strlen(s1);
    lb = strlen(s2);
    for(int i = 0; i < lb / 2; i++) swap(s2[i], s2[lb - 1 - i]);
    int mi = 0;
    while((1 << mi) < (max(la, lb) + 1)) mi++;
    len = (1 << (mi + 1));
    ///A串的a取1,B串的b取1
    memset(num, 0, sizeof(num));
    calc('S', 'P');
    calc('P', 'R');
    calc('R', 'L');
    calc('L', 'K');
    calc('K', 'S');

    calc('S', 'L');
    calc('L', 'P');
    calc('P', 'K');
    calc('K', 'R');
    calc('R', 'S');
    int ans = 0;
    for(int i = lb - 1; i < la; i++)
    {
        ans = max(ans, num[i]);
    }
    printf("%d
", ans);
    return 0;
}
Code

6.Educational Codeforces Round 40 Yet Another String Matching Problem

FFT + 并查集。

这个字符串处理的比前面的难了一些。

首先处理如何计算两个相同长度的串。

相同位的字母间连边,最后得到这样的图:

找到了一个好用的画图软件,再也不用画图板了。。。https://csacademy.com/app/graph_editor/

那最后的距离就是不同的节点个数 - 联通块个数 = 6 - 2 = 4,为什么呢

首先,我们先要明白,一个联通块的节点,最后都会变成同一个字母,

比如全变成e。那一个联通块内不同的节点就是总个数 - 1,所以总的就是 总个数 - 联通块个数。

现在再来考虑题目:

我们把每一次比较当成一个无向图,那么最多有 n - m + 1个无向图。

下面就像正常的FFT那样计算,比如 上面的串中'a'设为1,下面的串'b'设为1,FFT后,

系数为1的点,代表此无向图中,有a --> b这条边,因为只有a~f这6个字母,所以每个无向图的节点只有6个,直接跑并查集就可以了。

时间复杂度O(30nlogn)。

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <map>
#include <set>
using namespace std;
typedef unsigned long long ll;
const double PI = acos(-1.0);
const int maxn = (1 << 18) + 50;
struct Complex
{
    double real,image; ///实部和虚部
    Complex(double _real,double _image)
    {
        real = _real;
        image = _image;
    }
    Complex(){}
};

Complex operator + (const Complex &c1,const Complex &c2)
{
    return Complex(c1.real+c2.real,c1.image+c2.image);
}

Complex operator - (const Complex &c1,const Complex &c2)
{
    return Complex(c1.real-c2.real,c1.image-c2.image);
}

Complex operator * (const Complex &c1,const Complex &c2)
{
    return Complex(c1.real*c2.real-c1.image*c2.image, c1.real*c2.image + c1.image * c2.real);
}

int rev(int id,int len)
{
    int ret = 0;
    for(int i=0;(1<<i)<len;i++)
    {
        ret <<= 1;
        if(id & (1<<i)) ret |= 1;
    }
    return ret;
}
Complex A[maxn];
void DFT(Complex* a,int len,int flag) ///对a进行DFT或者逆DFT,结果存在a当中
{
    for(int i = 0; i < len; i++)
        A[rev(i,len)] = a[i]; ///按其在叶子节点中的顺序存储
    for(int s = 1; (1 << s)<= len; s++)
    {
        int m = (1 << s);
        Complex wm = Complex(cos(flag*2*PI/m),sin(flag*2*PI/m)); ///主n次单位根
        for(int k = 0; k < len; k += m)
        {
            Complex w = Complex(1, 0); ///旋转因子
            for(int j = 0; j < (m >> 1); j++)
            {
                Complex t = w * A[k + j + (m >> 1)];
                Complex u = A[k + j];
                A[k + j] = u + t;
                A[k + j + (m >> 1)] = u - t;
                w = w * wm;
            }
        }
    }
    if(flag == -1) for(int i = 0; i < len; i++) A[i].real /= len, A[i].image /= len;
    for(int i = 0; i < len; i++) a[i] = A[i];
    return;
}
///把每一位作为系数
///乘积后次数最大为2 * n - 2,转换成2的k次幂,(1<<17)<2*n-2<(1<<18)
Complex a[maxn];
Complex b[maxn];
int num[maxn];
void FFT(int len)
{
    DFT(a, len, 1);
    DFT(b, len, 1);
    for(int i = 0; i < len; i++)
        a[i] = a[i] * b[i];
    DFT(a, len, -1);
    for(int i = 0; i < len; i++)
        num[i] = (int)(a[i].real + 0.5);
}
char s1[maxn];
char s2[maxn];
int la, lb, len;
int pre[maxn][7];
void calc(char l, char r)
{
    for(int i = 0; i < len; i++)
    {
        if(i < la) a[i] = Complex((s1[i] == l ? 1 : 0), 0);
        else a[i] = Complex(0, 0);
        if(i < lb) b[i] = Complex((s2[i] == r ? 1 : 0), 0);
        else b[i] = Complex(0, 0);
    }
    FFT(len);
}
int Find(int i, int x)
{
    if(x == pre[i][x]) return x;
    return pre[i][x] = Find(i, pre[i][x]);
}
set<int> st[maxn];
int main()
{
    scanf("%s %s", s1, s2);
    la = strlen(s1);
    lb = strlen(s2);
    for(int i = 0; i < lb / 2; i++) swap(s2[i], s2[lb - 1 - i]);
    int mi = 0;
    while((1 << mi) < (max(la, lb) + 1)) mi++;
    len = (1 << (mi + 1));
    ///A串的a取1,B串的b取1
    for(int i = lb - 1; i < la; i++)
    {
        for(int j = 0; j < 6; j++)
        {
            pre[i][j] = j;
        }
    }
    memset(num, 0, sizeof(num));
    for(int i = 0; i < 6; i++)
    {
        for(int j = 0; j < 6; j++)
        {
            if(i == 0 && j == 3)
            {
                int ccc = 0;
            }
            int u = i, v = j;
            calc(u + 'a', v + 'a');
            for(int k = lb - 1; k < la; k++)
            {
                if(num[k]) ///代表有边
                {
                    int x = Find(k, u);
                    int y = Find(k, v);
                    pre[k][x] = pre[k][y];
                    st[k].insert(u);
                    st[k].insert(v);
                }
            }
        }
    }
    for(int i = lb - 1; i < la; i++)
    {
        int cnt = 0;
        for(int j = 0; j < 6; j++)
        {
            if(pre[i][j] == j && (st[i].count(j))) cnt++;
        }
        printf("%d ", st[i].size() - cnt);
    }
    return 0;
}
Code
原文地址:https://www.cnblogs.com/littlepear/p/8698672.html