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;
}