练习赛补题-------H - Rock Paper Scissors FFT

题意:给你两个字符串AB,求B在A中选一个起点开始匹配,能匹配到的位置(对应位置的RPS比大小,胜利的位置)的最大数量。
R:石头 P:布 S:剪刀

第一次知道FFT可以有这样的妙用,可以用于字符串匹配,求最大匹配数,这道题需要变一变;
FFT内部实现数学原理至今不懂,只知道是用于多项式乘法,优化成O(n * log(n))的复杂度,只是把FFT当成模板来使用。
首先处理一下两个字符串,将s1字符串内的R变为P,P变为S,S变为R,将s2字符串翻转;
之后对每个字符进行FFT,将s1字符串变为01串,s2字符串也变成01串,不是该字符的都是0;
为什么翻转呢?
因为多项式乘法最后答案是C(k) = a(0) * b(k) + a(1) * b(k - 1)+ …,因为字符串是正向匹配的,如果将匹配串翻转一下,会因为多项式的性质变为正向匹配,如果b串翻转一下,就变成了C(k) = a(0) * b(0) +…
因为匹配串的长度为m,所以最后取(m - 1)~ (n + m - 1)位置上的数即可,因为m - 2就不是从头开始匹配了。
这个方法太巧太妙了。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int inf = 0x3f3f3f3f;
const int N = 1e5 + 5;

// 这一大坨fft的代码实现不要动的
const double PI = acos(-1.0);
struct Complex {
    double x, y;
    Complex(double _x = 0.0, double _y = 0.0) {
        x = _x;
        y = _y;
    }
    Complex operator - (const Complex &b)const {
        return Complex(x-b.x, y-b.y);
    }
    Complex operator + (const Complex &b)const {
        return Complex(x+b.x, y+b.y);
    }
    Complex operator * (const Complex &b)const {
        return Complex(x*b.x-y*b.y, x*b.y+y*b.x);
    }
};

void change(Complex y[], int len) {
    int i, j, k;
    for(i = 1, j = len/2; i < len-1; i++) {
        if (i < j) swap(y[i], y[j]);
        k = len/2;
        while(j >= k) {
            j -= k;
            k /= 2;
        }
        if (j < k) j += k;
    }
}

void fft(Complex y[], int len, int on) {
    change(y, len);
    for(int h = 2; h <= len; h <<= 1) {
        Complex wn(cos(-on*2*PI/h), sin(-on*2*PI/h));
        for(int j = 0; j < len; j += h) {
            Complex w(1, 0);
            for(int k = j; k < j+h/2; k++) {
                Complex u = y[k];
                Complex t = w*y[k+h/2];
                y[k] = u+t;
                y[k+h/2] = u-t;
                w = w*wn;
            }
        }
    }
    if (on == -1)
        for(int i = 0; i < len; i++)
        y[i].x /= len;
}
//数组要开四倍,2n长度然后乘起来就变成4n长度的了
int num[N << 2];
char s1[N],s2[N];
int n,m;
Complex f1[N << 2],f2[N << 2];
int ans[N << 2];

void solve(char ch){
    int len1 = strlen(s1);
    int len2 = strlen(s2);
    int len = 1;
    while(len < 2 * len1 || len < 2 * len2) len <<= 1;
    for(int i = 0;i < len1;++i)
    {
        f1[i] = Complex(s1[i] == ch,0);
    }
    for(int i = len1;i < len;++i)
    {
        f1[i] = Complex(0,0);
    }
    fft(f1,len,1);
    for(int i = 0;i < len2;++i)
    {
        f2[i] = Complex(s2[i] == ch,0);
    }
    for(int i = len2;i < len;++i)
    {
        f2[i] = Complex(0,0);
    }
    fft(f2,len,1);
    for(int i = 0;i < len;++i)
    {
        f1[i] = f1[i] * f2[i];
    }
    fft(f1,len,-1);
    for(int i = 0; i < len;++i)
    {
        num[i] = (LL)(f1[i].x + 0.5);
    }
    len = len1 + len2 - 1;
    for(int i = 0;i < len;++i){
        ans[i] += num[i];
    }
}

int main()
{
    while(~scanf("%d %d",&n,&m))
    {
        scanf("%s %s",s1,s2);
        for(int i = 0;i < n;++i){
            if(s1[i] == 'R'){
                s1[i] = 'P';
            }else if(s1[i] == 'P'){
                s1[i] = 'S';
            }else{
                s1[i] = 'R';
            }
        }
        reverse(s2,s2 + m);
        memset(ans,0,sizeof(ans));
        solve('P');
        solve('S');
        solve('R');
        int MAX = 0;
        for(int i = m - 1;i < n + m - 1;++i){
            MAX = max(MAX,ans[i]);
        }
        printf("%d\n",MAX);
    }
    return 0;
}

发布了269 篇原创文章 · 获赞 33 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/qq_36386435/article/details/89044155