福建工程学院第十四届ACM校赛J题题解

第六集,想不到你这个浓眉大眼的都叛变革命了

题意:

给你两个只包含01的字符串S和T,问你在允许一次错误的情况下,T是否能成为S的子串

思路:

这个问题的解法挺多,我是用fft匹配的,也比较简单,针对0和1匹配两次,第一次针对0就是把S串和T串中等于0的位置都标记成1,然后reverse一个串后进行fft,如果这两个位置都是0,就会出现1*1=1的情况,代表有一个位置匹配上了,0这样做一次,1这样做一次,他们的和就是匹配成功的次数,所以允许一次错误就是判断和是否大于len-1。

还有一个做法是指数哈希,判断两个串的哈希值的差是否是2^n,如果是的话check一下,就做出来了,2^n可以塞到hash或者map里。

还有exkmp等其他做法你们自行了解一下

代码实现

给出fft的做法(我只写了fft)

#include <iostream>

#include <string>

#include <cstring>

#include <algorithm>

using namespace std;

typedef long long ll;

const int  N = 150000;

ll a[N],b[N];

const ll PMOD=(479<<21)+1;

const ll PR=3;

static ll qp[30];

ll res[N];

struct NTT__container

{

    NTT__container()

    {

        int  t,i;

        for( i=0; i<21; i++)///注意循环上界与2n次幂上界相同

        {

            t=1<<i;

            qp[i]=quick_pow(PR,(PMOD-1)/t);

        }

    }

    ll quick_pow(ll x,ll n)

    {

        ll ans=1;

        while(n)

        {

            if(n&1)

                ans=ans*x%PMOD;

            x=x*x%PMOD;

            n>>=1;

        }

        return ans;

    }

    int get_len(int n)///计算刚好比n大的2的N次幂

    {

        int i,len;

        for(i=(1<<30); i; i>>=1)

        {

            if(n&i)

            {

                len=(i<<1);

                break;

            }

        }

        return len;

    }

    inline void NTT(ll F[],int len,int type)

    {

        int id=0,h,j,k,t,i;

        ll E,u,v;

        for(i=0,t=0; i<len; i++)

        {

            if(i>t)   swap(F[i],F[t]);

            for(j=(len>>1); (t^=j)<j; j>>=1);

        }

        for( h=2; h<=len; h<<=1)

        {

            id++;

            for( j=0; j<len; j+=h)

            {

                E=1;

                for(int k=j; k<j+h/2; k++)

                {

                    u=F[k];

                    v=(E*F[k+h/2])%PMOD;

                    F[k]=(u+v)%PMOD;

                    F[k+h/2]=((u-v)%PMOD+PMOD)%PMOD;

                    E=(E*qp[id])%PMOD;

                }

            }

        }

        if(type==-1)

        {

            int i;

            ll inv;

            for(i=1; i<len/2; i++)

                swap(F[i],F[len-i]);

            inv=quick_pow(len,PMOD-2);

            for( i=0; i<len; i++)

                F[i]=(F[i]%PMOD*inv)%PMOD;

        }

    }

    inline void inv(ll *a,int len)///答案存在res中

    {

        if(len==1)

        {

            res[0]=quick_pow(a[0],PMOD-2);

            return ;

        }

        inv(a,len>>1);///递归

        static ll temp[N];

        memcpy(temp,a,sizeof(ll)*(len>>1));

        NTT(temp,len,1);

        NTT(res,len,1);

        int i;

        for(i=0; i<len; i++)

            res[i]=res[i]*(2-temp[i]*res[i]%PMOD+PMOD)%PMOD;

        NTT(res,len,-1);

        memset(res+(len>>1),0,sizeof(ll)*(len>>1));

    }

    void mul(ll x[],ll y[],int len)///答案存在x中

    {

        int i;

        NTT(x,len,1);///先映射到频域上

        NTT(y,len,1);///先映射到频域上

        for(i=0; i<len; i++)

            x[i]=(x[i]*y[i])%PMOD;///在频域上点积

        NTT(x,len,-1);///再逆变换回时域

    }

 

} cal;

ll x[N],y[N],z[N];

int main()

{

    ios::sync_with_stdio(false);

    cin.tie(0);

    int N;

    cin>>N;

    while(N--){

        string s1,s2;

        cin>>s1>>s2;

        if(s2.length()>s1.length()){

            puts("NO");

            continue;

        }

        int len=cal.get_len(s1.length());

        //做1匹配

        memset(x,0,len*sizeof(ll));

        memset(y,0,len*sizeof(ll));

        memset(z,0,len*sizeof(ll));

        for(int i=0;i<s1.length();i++){

            x[i]=s1[i]-'0';

        }

        for(int i=0;i<s2.length();i++){

            y[i]=s2[i]-'0';

        }

        reverse(y,y+s2.length());

        cal.mul(x,y,len);

        //for(int i=0;i<len;i++)

        for(int i=s2.length()-1;i<s1.length();i++)

            z[i]+=x[i];

        //做0匹配

        memset(x,0,len*sizeof(ll));

        memset(y,0,len*sizeof(ll));

        for(int i=0;i<s1.length();i++){

            x[i]=1-(s1[i]-'0');

        }

        for(int i=0;i<s2.length();i++){

            y[i]=1-(s2[i]-'0');

        }

        reverse(y,y+s2.length());

        cal.mul(x,y,len);

        //for(int i=0;i<len;i++)

        for(int i=s2.length()-1;i<s1.length();i++)

            z[i]+=x[i];

 

        bool flag=0;

        for(int i=s2.length()-1;i<s1.length();i++){

            if(z[i]>=s2.length()-1){

                flag=1;

                break;

            }

        }

        if(flag){

            puts("YES");

        }

        else {

            puts("NO");

        }

    }

}

猜你喜欢

转载自www.cnblogs.com/xseventh/p/10878253.html