bunoj 34990(hash)

 

传送门:Justice String

题意:有两个串A,B,问是否存在A的一个子串S,S和B的长度相等,最多有2个字符不同。如果有多个,输出其实下标最小S的下标,没有输出-1。

分析:从A每个位置开始找最长公共前缀,如果最长公共前缀长度不大于lenb,继续从下一次位置开始找,至多找两次,如果一直找不到就算不存在。

#include <cstdio>

#include <cstring>

#include <string>

#include <queue>

#include <cmath>

#include <algorithm>

#define LL long long

#define N 100010

using namespace std;

const LL mod=1000000007ll;

const LL SEED=131;

char a[N],b[N];

int lena,lenb;

unsigned LL hha[N],hhb[N];

unsigned LL p[N];

void init()

{

    hha[0]=a[0];

    hhb[0]=b[0];

    for(int i=1;i<lena;i++)

    {

        hha[i]=hha[i-1]*SEED+a[i];

    }

    for(int i=1;i<lenb;i++)

    {

        hhb[i]=hhb[i-1]*SEED+b[i];

    }

}

LL calc(int x,int len,unsigned LL f[])

{

    return f[x+len-1]-f[x-1]*p[len];

}

int lcp(int pa,int pb)

{

    int l=0,r=lenb-pb,ans;

    while(l<=r)

    {

        int m=(l+r)>>1;

        if(calc(pa,m,hha)==calc(pb,m,hhb))

            l=m+1,ans=m;

        else r=m-1;

    }

    return ans;

}

int main()

{

    int T,cas=1;

    p[0]=1ll;

    for(int i=1;i<N;i++)p[i]=p[i-1]*SEED;

    scanf("%d",&T);

    while(T--)

    {

        scanf("%s%s",a,b);

        lena=strlen(a);

        lenb=strlen(b);

        init();

        int ans=-1;

        for(int i=0;i<=lena-lenb;i++)

        {

            int num=0;

            num+=lcp(i+num,num);

            if(num>=lenb)

            {

                ans=i;break;

            }

            num++;

            if(num>=lenb)

            {

                ans=i;break;

            }

            num+=lcp(i+num,num);

            if(num>=lenb)

            {

                ans=i;break;

            }

            num++;

            if(num>=lenb)

            {

                ans=i;break;

            }

            num+=lcp(i+num,num);

            if(num>=lenb)

            {

                ans=i;break;

            }

        }

        printf("Case #%d: %d\n",cas++,ans);

    }

}
View Code

 

你可能感兴趣的:(hash)