2017ICPC Daejeon H-Rock Paper Scissors(FFT)

题意: 给你两个字符串s , t ( |t| < |s| )只包含三个字符'S','R','P'分别代表剪刀石头布,问如何匹配使得t胜场最多,输出最多胜场
·
·
·
容易想到把字符串t中的'S','R','P'分别变成'P','S','R',那原问题就转化为在s中找到一个长度为|t|的子串使得该串与t对应位置相同字符最多(这里与原问题并不完全等价,s的某一个长度小于|t|的后缀与t也可以匹配)
设|s| = n , |t| = m
考虑FFT,先把t转化之后再翻转一下,则就是求对于某个i

\[F(i+m-1)=\sum_{j=i}^{i+m-1}s[j]*t[m-(j-i)-1] \]

分别计算三个字符的贡献,当考虑某个字符时,把其他字符都置为0,该字符置为1,则若两个字符串某位上都是这个字符,则贡献1,所以F(i+m-1)就是表示从i开始的某个字符的贡献,可以记为ans[0][i]
那么最终答案就是

for i in n:
    max(ans[0][i]+ans[1][i]+ans[2][i])
#include <bits/stdc++.h>
using namespace std;
const int maxn = 300000;
typedef double LD;
int ans[3][maxn], n, m, len;
const LD PI = acos(-1);
struct C
{
    LD r, i;
    C(LD r = 0, LD i = 0) : r(r), i(i) {}
};
C operator+(const C &a, const C &b)
{
    return C(a.r + b.r, a.i + b.i);
}
C operator-(const C &a, const C &b)
{
    return C(a.r - b.r, a.i - b.i);
}
C operator*(const C &a, const C &b)
{
    return C(a.r * b.r - a.i * b.i, a.r * b.i + a.i * b.r);
}
 
void FFT(C 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 h = 2; h <= n; h <<= 1)
    {
        C wn(cos(p * 2 * PI / h), sin(p * 2 * PI / h));
        for (int i = 0; i < n; i += h)
        {
            C w(1, 0), u;
            for (int j = i, k = h >> 1; j < i + k; ++j)
            {
                u = x[j + k] * w;
                x[j + k] = x[j] - u;
                x[j] = x[j] + u;
                w = w * wn;
            }
        }
    }
    if (p == -1)
        for (int i = 0; i < n; ++i)
            x[i].r /= n;
}
 
void conv(C a[], C b[], int n)
{
    FFT(a, n, 1);
    FFT(b, n, 1);
    for (int i = 0; i < n; ++i)
        a[i] = a[i] * b[i];
    FFT(a, n, -1);
}
C f[maxn], g[maxn];
char s[maxn], t[maxn];
void gao(char ch,int id)
{
    for (int i = 0; i < n; i++)
    {
        if (s[i] == ch) f[i] = C(1, 0);
        else f[i] = C(0, 0);
    }
    for (int i = 0; i < m; i++)
    {
        if (t[i] == ch) g[i] = C(1, 0);
        else g[i] = C(0, 0);
    }
    for (int i = n; i < len; i++) f[i] = C(0, 0);
    for (int i = m; i < len; i++) g[i] = C(0, 0);
    conv(f, g, len);
    for (int i = 0; i <= n - 1; i++)
        ans[id][i] = (int)(f[i + m - 1].r + 0.5);
}
 
int main()
{
    scanf("%d%d", &n, &m);
    scanf("%s%s", s, t);
    for (int i = 0; i < m; i++)
    {
        if (t[i] == 'R') t[i] = 'S';
        else if (t[i] == 'S') t[i] = 'P';
        else t[i] = 'R';
    }
    strrev(t);
    for (len = 1; len <= m + n; len <<= 1);
    gao('S',1);gao('P',2);gao('R',0);
    int ret=0;
    for(int i=0;i<n;i++)
        ret=max(ret,ans[0][i]+ans[1][i]+ans[2][i]);
    printf("%d\n", ret);
    return 0;
}
posted @ 2019-11-01 21:27  Zeronera  阅读(116)  评论(0编辑  收藏  举报