[TJOI2017] DNA 解题报告 (hash+二分)

题目链接:https://www.luogu.org/problemnew/show/P3763

题目大意:

给定原串S0,询问S0有多少个子串和给定串S相差不到3个字母

题解:

我们枚举S0的子串,问题转化为如何高效的判断两个串是否相差不到三个字母

考虑到数据范围,发现只能有log的时间余地

自然想到二分

solve每次找到第一个不同的位置并且返回,连续solve 4次,若S在这期间被处理完了,那么说明两个串相差不到3个字母

当然还有一些小细节

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
typedef unsigned long long ull;
const int N=1e5+15; 
int lena,lenb;
ull pin[N],haa[N],hab[N];
char a[N],b[N];
void H()
{
    haa[0]=hab[0]=0;
    for (int i=1;i<=lena;i++)
    {
        haa[i]=haa[i-1]*113+a[i]-'a';
    }
    for (int i=1;i<=lenb;i++)
    {
        hab[i]=hab[i-1]*113+b[i]-'a';
    }
}
int solve(int x,int y)
{
    int l=1,r=lenb;
    while (l<r)
    {
        int mid=l+r>>1;
        if ((haa[x+mid-1]-haa[x-1]*pin[mid])==(hab[y+mid-1]-hab[y-1]*pin[mid]))
        {
            l=mid+1;
        }
        else r=mid;
    }
    if (haa[x+l-1]-haa[x-1]*pin[l]!=hab[y+l-1]-hab[y-1]*pin[l])
    {
        return l;
    }
    else return l+1;
}
int check(int x)
{
    int now=1,k; 
    for (int i=1;i<=3;i++)
    {
        k=solve(x,now);
        //printf("%d %d %d\n",x,now,k);
        x+=k;now+=k;
        if (now>lenb) return 1;
    }
    k=solve(x,now);
    x+=k-2;now+=k-2;
    if (now>=lenb) return 1;
    return 0;
}
int main()
{
    pin[0]=1;
    for (int i=1;i<=N;i++) pin[i]=pin[i-1]*113;
    int T;
    scanf("%d",&T);
    while (T--)
    {
        scanf("%s",a+1);
        scanf("%s",b+1);
        lena=strlen(a+1),lenb=strlen(b+1);
        H();
        //printf("%d\n",solve(2,3));
        int re=0;
        for (int i=1;i<=lena-lenb+1;i++)
        {
            if (check(i)) re++;
        }
        printf("%d\n",re);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/xxzh/p/9691942.html