整理的算法模板合集: ACM模板
实际上是一个全新的精炼模板整合计划
2021年度训练联盟热身训练赛第四场 H - Rock Paper Scissors(字符串匹配,FFT)
Weblink
https://ac.nowcoder.com/acm/contest/13506/H
Problem
你和电脑玩剪刀石头布,给定一个电脑的出拳序列 s s s 以及你的一个出拳序列 t t t ,你可以任意在电脑的出拳序列 s s s 里选择一个位置开始比赛直到序列结束,问你最多能赢多少场。
12 4
RSPPSSSRRPPR
RRRR
3
Solution
SB题直接秒了
显然先转换为字符串匹配,即将字符串里, R R R 赢 S S S ,问我们能赢多少场,所以把电脑的出拳序列里所有的 S S S 换成 R R R , P , S P,S P,S 同理,这样问题就可以转换为一个简单的字符串匹配了,然后考虑如何匹配,显然我们可以把三个分开来一个一个匹配这样匹配三次求和就行了,即每次匹配一个字母 ch
,这样简单易处理,两个字符串这一位都等于 ch
就将他置为 1,否则就置为 0。直接字符串匹配怕是失了智,这波啊,这波是模糊匹配啊,先玩一玩嘛
我们发现每次匹配(这里先匹配 S
):
0123456
RRSPRRS
SPSRS
0123
⬇
0123456
0010001
10101
01234
是 2 和 0 匹配,6 和 4 匹配,好像没什么意思,但是如果我们把 t t t 翻转一下,就会变成 2 和 4 匹配,6 和 0 匹配, 2 + 4 = 6 + 0 = 6 2+4=6+0 =6 2+4=6+0=6,欸,这不就是卷积嘛 ?!!
我们知道序列的卷积为:
C ( x ) = A ( x ) ∗ B ( x ) = ∑ k = 0 n + m − 2 ( ∑ k = i + j a i b j ) x k C(x)=A(x)* B(x)=\sum_{k=0}^{n+m-2}(\sum_{k=i+j}a_ib_j)x^k C(x)=A(x)∗B(x)=k=0∑n+m−2(k=i+j∑aibj)xk
也就是说我们只需要将 t t t 串翻转,然后直接卷(这样我们每次相当于匹配: s 0 , t m , s 1 , t m − 1 ⋯ s_0,t_m,s_1,t_{m-1}\cdots s0,tm,s1,tm−1⋯,答案就是 x 0 + m = x m x^{0+m}=x^m x0+m=xm 的系数,以此类推),答案就是三次卷积得到的序列的 i → m − 1 ∼ n + m − 2 i\to m-1\sim n+m-2 i→m−1∼n+m−2 中三个序列位置 i i i 的权值之和取最值。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 7, mod = 1e9 + 7;
const double PI = acos(-1.0);
int n, m;
int L, limit = 1;
int RR[N], ans[N];
struct Complex
{
double x, y;
Complex(double x = 0, double y = 0) : x(x), y(y) {
}
}sr[N], sp[N], ss[N], tr[N], tp[N], ts[N], ansr[N], ansp[N], anss[N];
Complex operator * (Complex J, Complex Q)
{
return Complex(J.x * Q.x - J.y * Q.y, J.x * Q.y + J.y * Q.x);
}
Complex operator - (Complex J, Complex Q)
{
return Complex(J.x - Q.x, J.y - Q.y);
}
Complex operator + (Complex J, Complex Q)
{
return Complex(J.x + Q.x, J.y + Q.y);
}
void FFT(Complex * A, int type)
{
for(int i = 0; i < limit; ++ i) {
if(i < RR[i])
swap(A[i], A[RR[i]]);
}
for(int mid = 1; mid < limit; mid <<= 1) {
Complex wn(cos(PI / mid), type * sin(PI / mid));
for(int len = mid << 1, pos = 0; pos < limit; pos += len) {
Complex w(1, 0);
for(int k = 0; k < mid; ++ k, w = w * wn) {
Complex x = A[pos + k];
Complex y = w * A[pos + mid + k];
A[pos + k] = x + y;
A[pos + mid + k] = x - y;
}
}
}
if(type == -1) {
for(int i = 0; i < limit; ++ i) {
A[i].x /= limit;
}
}
}
string s, t;
int main()
{
scanf("%d%d", &n, &m);
cin >> s >> t;
for(int i = 0; i < n; ++ i) {
if(s[i] == 'R') s[i] = 'P';
else if(s[i] == 'P') s[i] = 'S';
else if(s[i] == 'S') s[i] = 'R';
}
reverse(t.begin(), t.end());
limit = 1, L = 0;
while(limit <= n + m) L ++ , limit <<= 1;
for(int i = 0; i < limit; ++ i) {
RR[i] = (RR[i >> 1] >> 1) | ((i & 1) << (L - 1));
}
for(int i = 0; i < n; ++ i)
if(s[i] == 'R') sr[i].x = 1.0;
else sr[i].x = 0.0;
for(int i = 0; i < m; ++ i)
if(t[i] == 'R') tr[i].x = 1.0;
else tr[i].x = 0.0;
FFT(sr, 1);
FFT(tr, 1);
for(int i = 0; i <= limit; ++ i) {
ansr[i] = sr[i] * tr[i];
}
FFT(ansr, -1);
for(int i = 0; i < n; ++ i)
if(s[i] == 'P') sp[i].x = 1.0;
else sp[i].x = 0.0;
for(int i = 0; i < m; ++ i)
if(t[i] == 'P') tp[i].x = 1.0;
else tp[i].x = 0.0;
FFT(sp, 1);
FFT(tp, 1);
for(int i = 0; i <= limit; ++ i) {
ansp[i] = sp[i] * tp[i];
}
FFT(ansp, -1);
for(int i = 0; i < n; ++ i)
if(s[i] == 'S') ss[i].x = 1.0;
else ss[i].x = 0.0;
for(int i = 0; i < m; ++ i)
if(t[i] == 'S') ts[i].x = 1.0;
else ts[i].x = 0.0;
FFT(ss, 1);
FFT(ts, 1);
for(int i = 0; i <= limit; ++ i) {
anss[i] = ss[i] * ts[i];
}
FFT(anss, -1);
int maxx = -1;
for(int i = m - 1; i < n + m - 1; ++ i) {
maxx = max(maxx, (int)(ansr[i].x + 0.5) + (int)(ansp[i].x + 0.5) + (int)(anss[i].x + 0.5));
}
printf("%d\n", maxx);
return 0;
}
百度一搜发现原来这是FFT求字符串匹配的模板题呀,那没事了,是我的问题,我太菜了,写的FFT题太少了…