2021年度训练联盟热身训练赛第四场 H - Rock Paper Scissors(字符串匹配,FFT)

本文介绍了如何使用FFT(快速傅里叶变换)解决2021年度训练联盟热身训练赛第四场H题——Rock Paper Scissors的字符串匹配问题。通过转换出拳序列,将问题转化为模糊匹配,利用卷积求解,最终通过FFT实现高效算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

整理的算法模板合集: 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
  01230123456
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=0n+m2(k=i+jaibj)xk

也就是说我们只需要将 t t t 串翻转,然后直接卷,这样我们每次相当于匹配: s 0 , t m − 1 , s 1 , t m − 2 ⋯ s_0,t_m-1,s_1,t_{m-2}\cdots s0,tm1,s1,tm2,答案就是 x 0 + m − 1 = x m − 1 x^{0+m-1}=x^{m-1} x0+m1=xm1 的系数,以此类推,实际的含义就是 s 0 s_0 s0 t m − 1 t_{m-1} tm1 匹配,其中 t m − 1 t_{m-1} tm1就是翻转后的 t 0 t_0 t0,也就是实际上还是按照规则 s 0 s_0 s0 t 0 t_0 t0 匹配, s 1 s_1 s1 t 1 t_1 t1 匹配,翻转后只是序号变了,并不影响实际的匹配情况和最后的匹配答案,如果匹配成功,即 1 × 1 = 1 1\times 1=1 1×1=1,也就是贡献给 x m x^m xm 的系数+1,也就是匹配段为 0 ∼ m − 1 0\sim m-1 0m1 的答案+1 匹配失败显然就是 1 × 0 = 0  or  0 × 0 = 0 1\times 0=0 \ \text{or}\ 0\times 0=0 1×0=0 or 0×0=0。最后最优的答案显然就是三次卷积得到的答案序列: i → m − 1 ∼ n + m − 2 i\to m-1\sim n+m-2 im1n+m2 中三个序列相应位置 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题太少了…

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

繁凡さん

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值