FFT匹配字符串

FFT匹配字符串

无匹配符

在这里插入图片描述

void FFT_Match(char *s1,char *s2,int m,int n){
    for(int i=0;i<m;i++){
        a[i].real()=s1[i]-'a'+1;//减小字符串的匹配数,由字符串的范围来减
    }
    for(int i=0;i<n;i++){
        b[i].real()=s2[i]-'a'+1;
    }
    reverse(a,a+m);//反转匹配字符串
    double T=0;
    for(int i=0;i<m;i++){
        T+=a[i].real()*a[i].real();
    }
    double b2[maxN];
    b2[0]=b[0].real()*b[0].real();
    for(int i=1;i<n;i++){
        b2[i]=b2[i-1]+b[i].real()*b[i].real();
    }
    FFT(a,len,1);//系数化为点值表示
    FFT(b,len,1);
    for(int i=0;i<len;i++){
        a[i]=a[i]*b[i];
    }
    FFT(a,len,-1);//点值化为系数表示
    for(int k=0;k<=n-m;k++){
        double p=k>0?   T+b2[k+m-1]-b2[k-1]-2*(int)(a[k+m-1].real()/len+0.5)
                        :T+b2[k+m-1]-2*(int)(a[k+m-1].real()/len+0.5);
        if(p==0)
            printf("%d ",k);
    }
}
有匹配符在这里插入图片描述
void FFT_Match_tongpei(char *s1,char *s2,int m,int n){
    complex<double> p[maxN];
    reverse(s1,s1+m);//反转匹配字符串
    memset(A,0,len*sizeof(long long));
    memset(B,0,len*sizeof(long long));
    memset(p,0,len*sizeof(long long));
    for(int i=0;i<m;i++){
        A[i]=(s1[i]!='#')? (s1[i]-'a'+1):0;//减小字符串的匹配数,由字符串的范围来减
    }
    for(int i=0;i<n;i++){
        B[i]=(s2[i]!='#')? (s2[i]-'a'+1):0;
    }
    for(int i=0;i<len;i++){
        a[i].real()=A[i]*A[i]*A[i];
        a[i].imag()=0;
        b[i].real()=B[i];
        b[i].imag()=0;
    }
    FFT(a,len,1);//系数化为点值表示
    FFT(b,len,1);
    for(int i=0;i<len;i++){
        p[i]=p[i]+a[i]*b[i];
    }
    for(int i=0;i<len;i++){
        a[i].real()=A[i];
        a[i].imag()=0;
        b[i].real()=B[i]*B[i]*B[i];
        b[i].imag()=0;
    }
    FFT(a,len,1);//系数化为点值表示
    FFT(b,len,1);
    for(int i=0;i<len;i++){
        p[i]=p[i]+a[i]*b[i];
    }for(int i=0;i<len;i++){
        a[i].real()=A[i]*A[i];
        a[i].imag()=0;
        b[i].real()=B[i]*B[i];
        b[i].imag()=0;
    }
    FFT(a,len,1);//系数化为点值表示
    FFT(b,len,1);
    for(int i=0;i<len;i++){
        p[i]=p[i]-a[i]*b[i]-a[i]*b[i];
    }
    FFT(p,len,-1);
    for(int k=0;k<=n-m;k++){
        if((int)(p[k+m-1].real()/len+0.5)==0)
            printf("%d ",k);
    }
}
完整代码
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <complex> //复数模板头文件
using namespace std;
const int maxN=100010;
const double pi=3.1415926;
void FFT(complex<double> *a,int n,int inv);
int len,n1,n2,cnt,rev[maxN],c[maxN],sign=1;//合成函数
void trans(int c[],int n);
void FFT_Match(char *s1,char *s2,int m,int n);
void FFT_Match_tongpei(char *s1,char *s2,int m,int n);
int A[maxN],B[maxN];
complex<double> a[maxN],b[maxN];
int main()
{
    char s1[maxN],s2[maxN];
    while(~scanf("%s%s",s1,s2)){
        len=1;
        n1=strlen(s1);
        n2=strlen(s2);
        while(len<n1+n2)len<<=1;//将n化作c的系数值,n为2的倍数;
        //FFT_Match(s2,s1,n2,n1);//注意这里前者为匹配字符串模板
        FFT_Match_tongpei(s2,s1,n2,n1);
        memset(a, 0 , (len+10)*sizeof(complex<double>));
        memset(b, 0 , (len+10)*sizeof(complex<double>));
    }
    return 0;
}
void FFT(complex<double> *a,int n,int inv){
    int bit=0;
    while((1<<bit)<n)bit++;
    for(int i=0;i<n;i++){ //为每一个值作下标的颠倒
        rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
        if(i<rev[i])//防止又转换回来
            swap(a[i],a[rev[i]]);
    }//下标颠倒之后a[i]代表基本值,len为2时加并一步步叠加
    for(int mid=1;mid<n;mid<<=1){ //从最底层开始叠加
        complex<double> wn(cos(pi/mid),inv*sin(pi/mid));//每一层的单位根
        for(int i=0;i<n;i+=mid<<1){//给每一层分组,i是每组起始位置
            complex<double> w(1,0);//
            for(int j=0;j<mid;j++){//组内运算
                complex<double> x=a[i+j],y=w*a[i+j+mid];
                a[i+j]=x+y;a[i+j+mid]=x-y;
                w*=wn;//运算时取实数部分
            }
        }
    }
}
void FFT_Match(char *s1,char *s2,int m,int n){
    for(int i=0;i<m;i++){
        a[i].real()=s1[i]-'a'+1;//减小字符串的匹配数,由字符串的范围来减
    }
    for(int i=0;i<n;i++){
        b[i].real()=s2[i]-'a'+1;
    }
    reverse(a,a+m);//反转匹配字符串
    double T=0;
    for(int i=0;i<m;i++){
        T+=a[i].real()*a[i].real();
    }
    double b2[maxN];
    b2[0]=b[0].real()*b[0].real();
    for(int i=1;i<n;i++){
        b2[i]=b2[i-1]+b[i].real()*b[i].real();
    }
    FFT(a,len,1);//系数化为点值表示
    FFT(b,len,1);
    for(int i=0;i<len;i++){
        a[i]=a[i]*b[i];
    }
    FFT(a,len,-1);//点值化为系数表示
    for(int k=0;k<=n-m;k++){
        double p=k>0?   T+b2[k+m-1]-b2[k-1]-2*(int)(a[k+m-1].real()/len+0.5)
                        :T+b2[k+m-1]-2*(int)(a[k+m-1].real()/len+0.5);
        if(p==0)
            printf("%d ",k);
    }
}
void FFT_Match_tongpei(char *s1,char *s2,int m,int n){
    complex<double> p[maxN];
    reverse(s1,s1+m);//反转匹配字符串
    memset(A,0,len*sizeof(long long));
    memset(B,0,len*sizeof(long long));
    memset(p,0,len*sizeof(long long));
    for(int i=0;i<m;i++){
        A[i]=(s1[i]!='#')? (s1[i]-'a'+1):0;//减小字符串的匹配数,由字符串的范围来减
    }
    for(int i=0;i<n;i++){
        B[i]=(s2[i]!='#')? (s2[i]-'a'+1):0;
    }
    for(int i=0;i<len;i++){
        a[i].real()=A[i]*A[i]*A[i];
        a[i].imag()=0;
        b[i].real()=B[i];
        b[i].imag()=0;
    }
    FFT(a,len,1);//系数化为点值表示
    FFT(b,len,1);
    for(int i=0;i<len;i++){
        p[i]=p[i]+a[i]*b[i];
    }
    for(int i=0;i<len;i++){
        a[i].real()=A[i];
        a[i].imag()=0;
        b[i].real()=B[i]*B[i]*B[i];
        b[i].imag()=0;
    }
    FFT(a,len,1);//系数化为点值表示
    FFT(b,len,1);
    for(int i=0;i<len;i++){
        p[i]=p[i]+a[i]*b[i];
    }for(int i=0;i<len;i++){
        a[i].real()=A[i]*A[i];
        a[i].imag()=0;
        b[i].real()=B[i]*B[i];
        b[i].imag()=0;
    }
    FFT(a,len,1);//系数化为点值表示
    FFT(b,len,1);
    for(int i=0;i<len;i++){
        p[i]=p[i]-a[i]*b[i]-a[i]*b[i];
    }
    FFT(p,len,-1);
    for(int k=0;k<=n-m;k++){
        if((int)(p[k+m-1].real()/len+0.5)==0)
            printf("%d ",k);
    }
}
石头剪刀布问题

题设:想哥给出一个长度为n的序列,而叶姐给出长度为m的序列。1≤m<n≤100,0001≤m<n≤100,000。叶姐显然有特权,她可以选择跳过想哥序列的一段开头,才开始将RSP序列进行匹配,以寻求从这一位置开始最多获胜次数。
请你帮叶姐求出这一次数,这就是想哥请17级吃饭的次数。显然,R胜S,S胜P,P胜R。

解:FFT分别匹配三种情况

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <complex> //复数模板头文件
using namespace std;
const int maxN=1000000;
const double pi=3.1415926;
void FFT(complex<double> *a,int n,int inv);
int len,n1,n2,cnt,rev[maxN],c[maxN],sign=1;//合成函数
void trans(int c[],int n);
void FFT_Match(char *s1,char *s2,int m,int n);
void FFT_Match_tongpei(char *s1,char *s2,int m,int n);
int A[maxN],B[maxN];
complex<double> a[maxN],b[maxN];
complex<double> p[maxN];
int main()
{
    char s1[maxN],s2[maxN];
    scanf("%d%d",&n1,&n2);
    scanf("%s",s1);
    scanf("%s",s2);
    len=1;
    while(len<n1+n2)len<<=1;//将n化作c的系数值,n为2的倍数;
    FFT_Match_tongpei(s2,s1,n2,n1);
    //memset(a, 0 , (len+10)*sizeof(complex<double>));
    //memset(b, 0 , (len+10)*sizeof(complex<double>));
    return 0;
}
void FFT(complex<double> *a,int n,int inv){
    int bit=0;
    while((1<<bit)<n)bit++;
    for(int i=0;i<n;i++){ //为每一个值作下标的颠倒
        rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
        if(i<rev[i])//防止又转换回来
            swap(a[i],a[rev[i]]);
    }//下标颠倒之后a[i]代表基本值,len为2时加并一步步叠加
    for(int mid=1;mid<n;mid<<=1){ //从最底层开始叠加
        complex<double> wn(cos(pi/mid),inv*sin(pi/mid));//每一层的单位根
        for(int i=0;i<n;i+=mid<<1){//给每一层分组,i是每组起始位置
            complex<double> w(1,0);//
            for(int j=0;j<mid;j++){//组内运算
                complex<double> x=a[i+j],y=w*a[i+j+mid];
                a[i+j]=x+y;a[i+j+mid]=x-y;
                w*=wn;//运算时取实数部分
            }
        }
    }
}
void FFT_Match_tongpei(char *s1,char *s2,int m,int n){
    reverse(s1,s1+m);//反转匹配字符串
    //匹配R->S
    for(int i=0;i<m;i++){
        A[i]=(s1[i]=='R')? 1:0;//减小字符串的匹配数,由字符串的范围来减
    }
    for(int i=0;i<n;i++){
        B[i]=(s2[i]=='S')? 1:0;
    }
    for(int i=0;i<len;i++){
        a[i]=complex<double>(A[i],0);
        b[i]=complex<double>(B[i],0);
    }
    FFT(a,len,1);//系数化为点值表示
    FFT(b,len,1);
    for(int i=0;i<len;i++){
        p[i]=p[i]+a[i]*b[i];
    }
    //匹配S->P
    for(int i=0;i<m;i++){
        A[i]=(s1[i]=='S')? 1:0;//减小字符串的匹配数,由字符串的范围来减
    }
    for(int i=0;i<n;i++){
        B[i]=(s2[i]=='P')? 1:0;
    }
    for(int i=0;i<len;i++){
        a[i]=complex<double>(A[i],0);
        b[i]=complex<double>(B[i],0);
    }
    FFT(a,len,1);//系数化为点值表示
    FFT(b,len,1);
    for(int i=0;i<len;i++){
        p[i]=p[i]+a[i]*b[i];
    }
    //匹配P->R
    for(int i=0;i<m;i++){
        A[i]=(s1[i]=='P')? 1:0;//减小字符串的匹配数,由字符串的范围来减
    }
    for(int i=0;i<n;i++){
        B[i]=(s2[i]=='R')? 1:0;
    }
    for(int i=0;i<len;i++){
        a[i]=complex<double>(A[i],0);
        b[i]=complex<double>(B[i],0);
    }
    FFT(a,len,1);//系数化为点值表示
    FFT(b,len,1);
    for(int i=0;i<len;i++){
        p[i]=p[i]+a[i]*b[i];
    }
    FFT(p,len,-1);
    int minc=0;
    for(int k=0;k<=n-m;k++){
        //printf("%d=%d ",k,(int)(p[k+m-1].real()/len+0.5));
        if((int)(p[k+m-1].real()/len+0.5)>minc)
            minc=(int)(p[k+m-1].real()/len+0.5);
    }
    printf("%d",minc);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值