zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    化零为整WCF(5) 宿主Hosting(宿主在IIS, Application, WAS, WindowsService)
    使用Aspose.Cell控件实现Excel高难度报表的生成(三)
    Winform开发框架之动态指定数据表
    Winform分页控件之纯分页显示处理
    利用Aspose.Word控件实现Word文档的操作
    代码生成工具之Winform查询列表界面生成
    代码生成工具之界面快速生成
    Winform开发中手写签名的实现
    代码生成工具之数据库表及字段名称转义
    Winform开发框架之通用附件管理模块
  • 原文地址:https://www.cnblogs.com/littlepear/p/8698672.html
Copyright © 2011-2022 走看看