一道扩展kmp好题

不是一道扩展kmp模板题。

题意:给定两个字符串A和B,求B在A中出现了几次及每次的位置。两个串均由小写字母组成,但B串有通配符“*”,表示可以匹配任何非空字符。

B在A中出现过,就是B的每一位和A中的连续的一段对应相等。如果我们构造一个多项式:

ci=j=0m1(sin+j+1tj)2tj c i = ∑ j = 0 m − 1 ( s i − n + j + 1 − t j ) 2 · t j

那么如果 ci c i 等于零,那么也就说明了A串以 i i 结束的长度为 m 的串和B串相同。但是发现这个式子不是很好算,我们把B倒过来,那么:
ci=j=0m1(sijtj)2tj c i = ∑ j = 0 m − 1 ( s i − j − t j ) 2 · t j

然后把平方展开,发现可以用FFT啦。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long
#define cp comp
using namespace std;
struct comp{
    double x,y;
}h1[400010],h2[400010],g1[400010],g2[400010],ans[400010];
const double pi=acos(-1);
double aa[400010],bb[400010];
char s[100010],t[100010];
int rev[400010],len=1,bit,ksj[400010];
inline cp operator + (cp a,cp b)
{
    cp ans;
    ans.x=a.x+b.x;ans.y=a.y+b.y;
    return ans;
}
inline cp operator - (cp a,cp b)
{
    cp ans;
    ans.x=a.x-b.x;ans.y=a.y-b.y;
    return ans;
}
inline cp operator * (cp a,cp b)
{
    cp ans;
    ans.x=a.x*b.x-a.y*b.y;ans.y=a.x*b.y+a.y*b.x;
    return ans;
}
inline void FFT(cp *a,int t)
{
    for(int i=0;i<len;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
    for(int step=1;step<len;step<<=1)
    {
        cp wn={cos(2*pi/step/2),sin(2*pi*t/step/2)};
        for(int j=0;j<len;j+=(step<<1))
        {
            cp wnk={1,0};
            for(int k=j;k<j+step;k++)
            {
                cp x=a[k],y=wnk*a[k+step];
                a[k]=x+y,a[k+step]=x-y;
                wnk=wnk*wn;
            }
        }
    }
    if(t==-1) for(int i=0;i<len;i++) a[i].x/=len;
}
int main()
{
    scanf("%s%s",s,t);
    double tmp=0;
    int n=strlen(s),m=strlen(t);
    while(len<(n+m)) len<<=1,bit++;
    for(int i=0;i<=len;i++)
        rev[i]=(rev[i>>1]>>1)|((i&1)<<bit-1);
    for(int i=0;i<n;i++) aa[i]=s[i]-'a'+1;
    for(int i=0;i<m;i++) if(t[i]=='*') bb[m-i-1]=0; else bb[m-i-1]=t[i]-'a'+1;
    for(int i=0;i<n;i++) h2[i].x=aa[i]*aa[i],h1[i].x=2*aa[i];
    for(int i=0;i<m;i++) g1[i].x=bb[i],g2[i].x=bb[i]*bb[i],tmp+=bb[i]*bb[i]*bb[i];
    FFT(h1,1),FFT(h2,1),FFT(g1,1),FFT(g2,1);
    for(int i=0;i<len;i++) ans[i]=(h2[i]*g1[i])-(h1[i]*g2[i]);
    FFT(ans,-1);
    int cnt=0;
    for(int i=0;i<len;i++) if(floor(ans[i].x+0.5+tmp)==0) ksj[++cnt]=i-m+1;
    cout<<cnt<<endl;
    for(int i=1;i<=cnt;i++) cout<<ksj[i]<<' ';
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值