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
",cas++,ans);
    }
}
View Code
原文地址:https://www.cnblogs.com/lienus/p/4389953.html