2017-2018 ACM-ICPC, Asia Daejeon Regional Contest H.剪刀石头布 (FFT,卷积)

题意:
给一个s串 一个t串,串中只有三种字母 分别代表剪刀石头布,问t串和s串中那个匹配使t串赢得最多。

fft加速卷积解模式串匹配的入门题。
考虑t出剪刀 s出布的情况:
将t中剪刀全部置1,s中布全部置1 其他置0。然后将t倒置,FFT(s,t)后得出的 x^i的系数 = (t1si-1 +t2si-2 ……) 即与某一位对齐时的 出剪刀的赢得场次
其余情况同理。
三次FFT 最后相加找出最大的系数即可。


#include<bits/stdc++.h>
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define endl '\n'
#define for1(I, A, B) for (int I = (A); I < (B); ++I)
#define forn(I, A, B) for (int I = (A); I <= (B); ++I)
#define Mod(a,b) a<b?a:a%b+b
#define rep(ii,a,b) for(int ii=a;ii<=b;++ii)
#define show(x) cout<<#x<<"="<<x<<endl
#define showa(a,b) cout<<#a<<'['<<b<<"]="<<a[b]<<endl
#define show2(x,y) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<endl
#define show3(x,y,z) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
#define show4(w,x,y,z) cout<<#w<<"="<<w<<" "<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
#define show5(v,w,x,y,z) cout<<#v<<"="<<v<<" "<<#w<<"="<<w<<" "<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
#define showmm(x,a,b) rep(i,0,a) rep(j,0,b) cout<<#x<<'['<<i<<']'<<'['<<j<<"]="<<x[i][j]<<(" \n"[j==b])
#define showm(x,a,b) rep(i,0,a) rep(j,0,b) cout<<x[i][j]<<(" \n"[j==b])
#define showa1(x,a,b) cout<<#x<<":\n";rep(i,a,b) showa(x,i);cout<<endl
#define showa2(x,a,b) cout<<#x<<": ";rep(i,a,b) cout<<x[i]<<' ';cout<<endl
using namespace std;
typedef long long ll;
typedef vector<int> vi;
typedef set<int> si;
typedef double db;
const db eps=1e-8;
const db Pi=acos(-1);
const ll inf=0x3f3f3f3f3f3f3f3f;
const int INF=0x3f3f3f3f;
const int MAX=1e6+10;
const ll mod=1e9+7;
ll gcd(ll a,ll b)
{
    return b?gcd(b,a%b):a;
}
const int MAXN=1e6+10;
 
struct cp
{
    double x,y;
    cp (double xx=0,double yy=0)
    {
        x=xx,y=yy;
    }
} a[MAXN],b[MAXN];
cp operator + (cp a,cp b)
{
    return cp(a.x+b.x, a.y+b.y);
}
cp operator - (cp a,cp b)
{
    return cp(a.x-b.x, a.y-b.y);
}
cp operator * (cp a,cp b)
{
    return cp(a.x*b.x-a.y*b.y, a.x*b.y+a.y*b.x);
}
int N,M;
int l,r[MAXN];
int limit=1;
void fast_fast_tle(cp *A,int type)
{
    for(int i=0; i<limit; i++)
        if(i<r[i]) swap(A[i],A[r[i]]);//@求出要迭代的序列@
    for(int mid=1; mid<limit; mid<<=1) //@待合并区间的中点@
    {
        cp Wn( cos(Pi/mid), type*sin(Pi/mid) );  //@单位根@
        for(int R=mid<<1,j=0; j<limit; j+=R) //@R是区间的右端点,j表示前已经到哪个位置了@
        {
            cp w(1,0);//@幂@
            for(int k=0; k<mid; k++,w=w*Wn) //@枚举左半部分@
            {
                cp x=A[j+k],y=w*A[j+mid+k];//@蝴蝶@
                A[j+k]=x+y;
                A[j+mid+k]=x-y;
            }
        }
    }
}
ll ans[MAX];
int main()
{
    IO;
    cin>>N>>M;
    while(limit<=N+M) limit<<=1,l++;
    for(int i=0; i<limit; i++)
        r[i]= ( r[i>>1]>>1 )| ( (i&1)<<(l-1) ) ;
    string s1,s2;
 
    cin>>s1>>s2;
    reverse(s2.begin(),s2.end());
    for1(i,0,N)if(s1[i]=='R')a[i].x = 1;
    for1(i,0,M)if(s2[i]=='P')b[i].x = 1;
    fast_fast_tle(a,1);
    fast_fast_tle(b,1);
    for(int i=0; i<=limit; i++) a[i]=a[i]*b[i];
    fast_fast_tle(a,-1);
    for(int i=0; i<=N; i++)
    {
        ans[i]+=(ll)(a[i+M-1].x/limit+0.5);
    }
    memset(a,0,sizeof(a));
    memset(b,0,sizeof(b));
    for1(i,0,N)if(s1[i]=='S')a[i].x = 1;
    for1(i,0,M)if(s2[i]=='R')b[i].x = 1;
    fast_fast_tle(a,1);
    fast_fast_tle(b,1);
    for(int i=0; i<=limit; i++) a[i]=a[i]*b[i];
    fast_fast_tle(a,-1);
    for(int i=0; i<=N; i++)
    {
        ans[i]+=(ll)(a[i+M-1].x/limit+0.5);
    }
    memset(a,0,sizeof(a));
    memset(b,0,sizeof(b));
    for1(i,0,N)if(s1[i]=='P')a[i].x = 1;
    for1(i,0,M)if(s2[i]=='S')b[i].x = 1;
    fast_fast_tle(a,1);
    fast_fast_tle(b,1);
    for(int i=0; i<=limit; i++) a[i]=a[i]*b[i];
    fast_fast_tle(a,-1);
    for(int i=0; i<=N; i++)
    {
        ans[i]+=(ll)(a[i+M-1].x/limit+0.5);
    }
    ll ANS=0;
    for(int i=0;i<=N;i++)
    {
        ANS=max(ANS,ans[i]);
    }
    cout<<ANS<<endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值