2021“MINIEYE杯”中国大学生算法设计超级联赛(3)

目录

Forgiving Matching(FFT+字符串匹配)


Forgiving Matching(FFT+字符串匹配)

题意:

给你两个串a,b长度分别为n,m;a中长度为m的子串与b进行匹配,其中∗代表通配符,
即与任何其他字符相同;当小Q可以原谅字串b有0,1,2,……T个位置可以是不匹配的,
分别输出有a中有多少子串是完全匹配的。

思路:

假如没有通配符,对于每个字符 c ,如果s[i]=t[j]=c,那么f[i+m-j]就应该加1 ( 以子串
右边界单独标记一个子串 ),而这里若是对字符串 t 翻转一下,m-j就对应j,那么i+m-j就
变成了i+j了(i+j=k,f[k]就是以 k 为右边界的子串的匹配位置数 ),而这时候我们就可
以通过FFT求出字符 c 对应的 f 了( 也就是求子串与T对应位置都是字符c的位置数)。FFT
字符串匹配,我们可以枚举每个字符,如果当前字符等于我们枚举的字符,我们就让多项式的
系数为1,否则为零。每次两个多项式相乘,每次累加,就是每个子串的匹配数 。

对于通配符,S的子串通过使用通配符来匹配的位置数 = S子串的通配符数 + T的通配符数 - 
两者对应位置都是通配符的位置数 。 S 子串的通配符数可以通过前缀和解决,T 的通配符数
可以直接遍历求和,两者对应位置都是通配符的位置数,这就跟上面的对应位置都为字符 c 
是一样的做法,用FFT求解即可 。求出来的 f 数组,进行m-f[i](m−1<=i<=n)处理,得到最
少需要 k 个才能匹配的子串各有多少个,然后对此求前缀和就是所求答案。( S 子串的通配
符数 与 T 的通配符数 通过FFT也可以求 ,就是慢,不过应该也可以过。。。)

#include<bits/stdc++.h>

using namespace std;
typedef unsigned long long ull;
typedef long long LL;
typedef pair<int,int> PII;
#define re register
inline int read()
{
    int ans=0;
    char last=' ',ch=getchar();
    while(ch<'0'|ch>'9')last=ch,ch=getchar();
    while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
    if(last=='-')ans=-ans;
    return ans;
}
int buf[105];
inline void write(int i)
{
    int p = 0;
    if(i == 0) p++;
    else while(i)
        {
            buf[p++] = i % 10;
            i /= 10;
        }
    for(int j = p-1; j >= 0; j--) putchar('0' + buf[j]);
}
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
const int maxn=1e6+7;

const double PI = acos(-1.0);
struct Complex
{
    double x,y;
    Complex(double _x = 0.0,double _y = 0.0)
    {
        x = _x;
        y = _y;
    }
    Complex operator-(const Complex &b)const
    {
        return Complex(x - b.x,y - b.y);
    }
    Complex operator+(const Complex &b)const
    {
        return Complex(x + b.x,y + b.y);
    }
    Complex operator*(const Complex &b)const
    {
        return Complex(x*b.x - y*b.y,x*b.y + y*b.x);
    }
};
/*
*进行FFT和IFFT前的反置变换
*位置i和i的二进制反转后的位置互换
*len必须为2的幂
*/
void change(Complex y[],int len)
{
    int i,j,k;
    for(int i = 1,j = len/2; i<len-1; i++)
    {
        if(i < j)    swap(y[i],y[j]);
        //交换互为小标反转的元素,i<j保证交换一次
        //i做正常的+1,j做反转类型的+1,始终保持i和j是反转的
        k = len/2;
        while(j >= k)
        {
            j = j - k;
            k = k/2;
        }
        if(j < k)    j+=k;
    }
}
/*
*做FFT
*len必须是2^k形式
*on == 1时是DFT,on == -1时是IDFT
*/
void fft(Complex y[],int len,int on)
{
    change(y,len);
    for(int h = 2; h <= len; h<<=1)
    {
        Complex wn(cos(-on*2*PI/h),sin(-on*2*PI/h));
        for(int j = 0; j < len; j += h)
        {
            Complex w(1,0);
            for(int k = j; k < j + h/2; k++)
            {
                Complex u = y[k];
                Complex t = w*y[k + h/2];
                y[k] = u + t;
                y[k + h/2] = u - t;
                w = w*wn;
            }
        }
    }
    if(on ==-1)
    {
        for(int i = 0; i < len; i++)
        {
            y[i].x /= len;
        }
    }
}
const int MAXN = 2e6+10;
Complex a[MAXN],b[MAXN],p[MAXN];
char str1[MAXN/2],str2[MAXN/2];
int sum[MAXN],f[maxn];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
         memset(sum,0,sizeof(sum));
        memset(f,0,sizeof(f));
        int n,m;
        n=read();
        m=read();
        scanf("%s%s",str1,str2);
        reverse(str2,str2+m);
        int len=1;
        while(len<=m+n-2) len<<=1;
        for(int i=0; i<=len; i++) p[i]=Complex(0,0),f[i]=0;
        //枚举每一个字符匹配的位置多少
        for(char c=0; c<=9; c++)
        {
            for(int i=0; i<len; i++)
            {
                if(i<n&&str1[i]-'0'==c)
                {
                    a[i]=Complex(1,0);
                }
                else
                {
                    a[i]=Complex(0,0);
                }
            }
            for(int i=0; i<len; i++)
            {
                if(i<m&&str2[i]-'0'==c)
                {
                    b[i]=Complex(1,0);
                }
                else
                {
                    b[i]=Complex(0,0);
                }
            }
            fft(a,len,1);
            fft(b,len,1);
            for(int i=0; i<len; i++) p[i]=p[i]+a[i]*b[i];
        }
        //
        //求当前字串T通用符与S串匹配的位置数
        for(int i=0; i<len; i++)
        {
            if(i<n)
            {
                a[i]=Complex(1,0);
            }
            else
            {
                a[i]=Complex(0,0);
            }
        }
        for(int i=0; i<len; i++)
        {
            if(i<m&&str2[i]=='*')
            {
                b[i]=Complex(1,0);
            }
            else
            {
                b[i]=Complex(0,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通用符与T串匹配的位置数
        for(int i=0; i<len; i++)
        {
            if(i<n&&str1[i]=='*')
            {
                a[i]=Complex(1,0);
            }
            else
            {
                a[i]=Complex(0,0);
            }
        }
        for(int i=0; i<len; i++)
        {
            if(i<m)
            {
                b[i]=Complex(1,0);
            }
            else
            {
                b[i]=Complex(0,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串和T串的都是通用符的匹配位置数
        for(int i=0; i<len; i++)
        {
            if(i<n&&str1[i]=='*')
            {
                a[i]=Complex(1,0);
            }
            else
            {
                a[i]=Complex(0,0);
            }
        }
        for(int i=0; i<len; i++)
        {
            if(i<m&&str2[i]=='*')
            {
                b[i]=Complex(1,0);
            }
            else
            {
                b[i]=Complex(0,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);
        for(int i=0; i<len; i++)
        {
            double sum1=(p[i].x+0.5);
            f[i]=int(sum1);
        }

        for(int i=m-1; i<n; i++)
        {
            sum[m-f[i]]++;
        }
        for(int i=1;i<=m;i++)
        {
            sum[i]+=sum[i-1];
        }
        for(int i=0;i<=m;i++)
        {
            printf("%d\n",sum[i]);
        }
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值