HDU - 6975 - Forgiving Matching ( FFT字符串匹配 )

47 篇文章 0 订阅
27 篇文章 0 订阅
题目链接:点击进入
题目

在这里插入图片描述
在这里插入图片描述

题意

给你两个字符串 S 、T ( 只包含 字符 0 - 9 以及 * ), S 长 n , T 长 m , * 是通配符( 即可以与任何字符匹配 ),问在匹配中最多允许 k( 0 - m )个不同位置的情况下,S 中最多有多少个能与 T 匹配的子串。

思路

对于 S 的所有长度为 m 的子串,维护一个 f 记录这个子串与 T 匹配的位置数,知道了 f 也就是知道了这个子串完全匹配所需要的最小的 k 。
假如没有通配符,对于每个字符 c ,如 果 s i s_i si = t j t_j tj = c , 那么 f i + m − j f_{i+m-j} fi+mj 就应该加 1 ( 以子串右边界单独标记一个子串 ),而这里若是对字符串 T 翻转一下, m - j 就对应 j ,那么 i + m - j 就变成了 i + j 了 ( i + j = k , f k f_k fk 就是以 k 为右边界的子串的匹配位置数 ),而这时候我们就可以通过FFT求出字符 c 对应的 f 了( 也就是求子串与T对应位置都是字符c的位置数)。枚举所有的 c ,然后对应系数相加,就是不带通配符的 f 。
对于通配符,S的子串通过使用通配符来匹配的位置数 = S子串的通配符数 + T的通配符数 - 两者对应位置都是通配符的位置数 。 S 子串的通配符数可以通过前缀和解决,T 的通配符数可以直接遍历求和,两者对应位置都是通配符的位置数,这就跟上面的对应位置都为字符 c 是一样的做法,用FFT求解即可 。
求出来的 f 数组,进行 m - f i ( m − 1 < = i < = n ) f_i(m-1<=i<=n) fi(m1<=i<=n)处理,得到最少需要 k 个才能匹配的子串各有多少个,然后对此求前缀和就是所求答案。
( S 子串的通配符数 与 T 的通配符数 通过FFT也可以求 ,就是慢,不过应该也可以过。。。)

代码
#include<iostream>
#include<string>
#include<map>
#include<set>
//#include<unordered_map>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<stack>
#include<algorithm>
#include<iomanip>
#include<cmath>
#include<fstream>
#define X first
#define Y second
#define best 131 
#define INF 0x3f3f3f3f3f3f3f3f
#define pii pair<int,int>
#define lowbit(x) x & -x
#define inf 0x3f3f3f3f
//#define int long long
//#define double long double
//#define rep(i,x,y) for(register int i = x; i <= y;++i)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double pai=acos(-1.0);
const int maxn=2e6+10;
const int mod=998244353;
const double eps=1e-7;
const int N=5e3+10;

inline int read()
{
    int k = 0, f = 1 ;
    char c = getchar() ;
    while(!isdigit(c)){if(c == '-') f = -1 ;c = getchar() ;}
    while(isdigit(c)) k = (k << 1) + (k << 3) + c - 48 ,c = getchar() ;
    return k * f ;
}

int n,m,k,ans[maxn],f[maxn],sums[maxn],sumt;
int lim=1,len,rev[maxn]; 
struct node
{
    double x,y;
    node(double xx=0,double yy=0){x=xx,y=yy;}
    node operator * (node Q){return node(x*Q.x-y*Q.y,x*Q.y+y*Q.x);}
    node operator + (node Q){return node(x+Q.x,y+Q.y);}
    node operator - (node Q){return node(x-Q.x,y-Q.y);}
}a[maxn],b[maxn],p[maxn];

inline void fft(node *A,double flag)
{
    for(int i=0;i<lim;i++)
        if(i<rev[i]) 
            swap(A[i],A[rev[i]]);
    for(int i=1;i<lim;i<<=1)
    {
        node wn(cos(pai/i),flag*sin(pai/i));
        for(int j=0;j<lim;j+=(i<<1))
        {
            node w(1,0);
            for(int k=0;k<i;k++,w=w*wn) 
            {
                node nx=A[j+k],ny=w*A[j+i+k];
                A[j+k]=nx+ny;
                A[j+i+k]=nx-ny ;
            }
        }
    }
}

char s[maxn],t[maxn];

int main()
{
//    ios::sync_with_stdio(false);
    scanf("%d",&k);
    while(k--)
    {
        scanf("%d%d",&n,&m);
        scanf("%s%s",s,t);
        
        reverse(t,t+m);
        
        while(lim<=m+n-2) lim<<=1,len++;
           for(int i=0;i<=lim;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(len-1));
        for(int i=0;i<=lim;i++) p[i]=node(0,0),ans[i]=0,f[i]=0;
        
        for(int c=0;c<=9;c++)//对应位置都是 c 的位置数
        {
            for(int i=0;i<=lim;i++) a[i]=node((i<n&&s[i]-'0'==c)?1:0,0);
            for(int i=0;i<=lim;i++) b[i]=node((i<m&&t[i]-'0'==c)?1:0,0);
            fft(a,1);
            fft(b,1);
            for(int i=0;i<=lim;i++) p[i]=p[i]+a[i]*b[i];
        }
        
//        for(int i=0;i<=lim;i++) a[i]=node((i<n&&s[i]=='*')?1:0,0);
//        for(int i=0;i<=lim;i++) b[i]=node((i<m)?1:0,0);
//        fft(a,1);
//        fft(b,1);
//        for(int i=0;i<=lim;i++) p[i]=p[i]+a[i]*b[i];
//        
//        for(int i=0;i<=lim;i++) a[i]=node((i<n)?1:0,0);
//        for(int i=0;i<=lim;i++) b[i]=node((i<m&&t[i]=='*')?1:0,0);
//        fft(a,1);
//        fft(b,1);
//        for(int i=0;i<=lim;i++) p[i]=p[i]+a[i]*b[i];

        for(int i=0;i<n;i++) sums[i+1]=0;
        sumt=0;
        
        for(int i=0;i<n;i++) //S子串的通配符数
        {
            if(s[i]=='*') sums[i+1]=1;
            sums[i+1]+=sums[i];
        }
        for(int i=0;i<m;i++) //T的通配符数
            if(t[i]=='*') 
                sumt++;
        fft(p,-1);
        for(int i=0;i<=lim;i++)
            f[i]+=(int)(p[i].x/lim+0.5);

        for(int i=0;i<=lim;i++) p[i]=node(0,0);//对应位置都是通配符的位置数
        for(int i=0;i<=lim;i++) a[i]=node((i<n&&s[i]=='*')?1:0,0);
        for(int i=0;i<=lim;i++) b[i]=node((i<m&&t[i]=='*')?1:0,0);
        fft(a,1);
        fft(b,1);
        for(int i=0;i<=lim;i++) p[i]=a[i]*b[i];
        
        fft(p,-1);
        for(int i=0;i<=lim;i++)
            f[i]-=(int)(p[i].x/lim+0.5);//减去对应位置都是通配符的位置数
        for(int i=m-1;i<n;i++)
        {
            int tmp=f[i]+sums[i+1]-sums[i-m+1]+sumt;
            ans[m-tmp]++;
        }
        for(int i=1;i<=m;i++)
            ans[i]+=ans[i-1];
        for(int i=0;i<=m;i++)
            printf("%d\n",ans[i]);
    }
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值