[GDOI2013][JZOJ3277]哈希和

来源:互联网 发布:sql unique啥意思 编辑:程序博客网 时间:2024/05/24 02:28

题目大意

设字符串str长度为l,定义字符串s的哈希值为

hash(str)=i=0l1c(stri)×26l1i

其中c(stri)表示字符striASCII码与字符aASCII码的差值。
给定字符串s,有m个询问,查询所有不重复的子串中,排名第x的到排名第y的子串哈希值之和,保证存在这么多不同的子串。

1|s|,m100000


题目分析

本题的难点在于如何求出哈希值的和,其它的我们直接离线,将询问差分,然后存在一个桶里面,排个序,求答案时我们直接按照排序后的顺序扫一遍,将可处理的询问处理了。
那么哈希值之和怎么求呢?
我们定义如下数组

hashi=hash(s0..i)sumi=sumi1+hashipi=j=1i+126j

lr内以l开头的子串的哈希和就为
sumrsuml1prlhashl1

因为前缀和相减之后,剩下的值就是原串最左端到区间内所有位置组成的的子串的哈希和,显然和这个区间最左端开始的子串多出同样的字符串,都是原串最左端到区间最左端的左端组成的子串,那么我们将他乘上差的几位幂数和,减掉就行了。
具体细节请读者自行思考,详见代码实现。


代码实现

#include <algorithm>#include <iostream>#include <cstring>#include <cstdio>using namespace std;typedef long long LL;const int N=100050;const int M=100050;const int P=12580;const int D=26;struct Q{    LL K;    int id;}qu[N<<1];bool operator<(Q a,Q b){    return a.K<b.K;}int Ws[N],Wv[N],x[N],y[N],SA[N],rank[N],height[N],sum[N],p[N],hash[N],ans[M][3];bool deal[M];LL q[M][2];char s[N];int n,m;void pre(){    p[0]=D;    for (int i=1,j=D*D%P;i<=n;i++,(j*=D)%=P)        p[i]=(p[i-1]+j)%P;    hash[0]=s[0]-'a';    for (int i=1;i<n;i++)        hash[i]=((LL)hash[i-1]*D%P+(s[i]-'a'))%P;    sum[0]=hash[0];    for (int i=1;i<n;i++)        sum[i]=(sum[i-1]+hash[i])%P;}bool cmp(int *r,int i,int j,int l){    return r[i]==r[j]&&r[i+l]==r[j+l];}void DA(){    int mx=0,l=1,p=0,i;    for (i=0;i<n;i++)mx=max(mx,Wv[i]=x[i]=s[i]-'a');    for (i=0;i<=mx;i++)Ws[i]=0;    for (i=0;i<n;i++)Ws[Wv[i]]++;    for (i=1;i<=mx;i++)Ws[i]+=Ws[i-1];    for (i=n-1;i>=0;i--)SA[--Ws[Wv[i]]]=i;    for (;l<=n&&p!=n;l<<=1)    {        for (p=0,i=n-l;i<n;i++)y[p++]=i;        for (i=0;i<n;i++)if(SA[i]>=l)y[p++]=SA[i]-l;        for (mx=0,i=0;i<n;i++)mx=max(mx,Wv[i]=x[y[i]]);        for (i=0;i<=mx;i++)Ws[i]=0;        for (i=0;i<n;i++)Ws[Wv[i]]++;        for (i=1;i<=mx;i++)Ws[i]+=Ws[i-1];        for (i=n-1;i>=0;i--)SA[--Ws[Wv[i]]]=y[i];        for (i=0;i<n;i++)y[i]=x[i],x[i]=0;        for (p=0,x[SA[0]]=0,i=1;i<n;i++)x[SA[i]]=cmp(y,SA[i-1],SA[i],l)?p:++p;    }    for (i=0;i<n;i++)rank[SA[i]]=i;}void get_height(){    for (int k=0,i=0;i<n;i++)    {        k?k--:k;        if (!rank[i])            continue;        int j=SA[rank[i]-1];        while (i+k<n&&j+k<n&&s[i+k]==s[j+k])            k++;        height[rank[i]]=k;    }}int hashsum(int st,int en){    if (st>en)        return 0;    return (((LL)sum[en]-(st?sum[st-1]:0)-(LL)(st?hash[st-1]:0)*p[en-st]%P)%P+P)%P;}int prefixsum(int st,int en1,int en2){    return ((hashsum(st,en2)-hashsum(st,en1-1))%P+P)%P;}void calc(){    int ptr=1,cnt=0;    LL kth=0,las=0;    for (int i=0;i<n;i++)    {        las=kth;        kth+=n-SA[i]-height[i];        while (ptr<=m<<1&&qu[ptr].K<=kth)        {            int j=qu[ptr].id,l=deal[j]?2:1;            deal[j]=1;            if (!qu[ptr].K)                ans[j][l]=0;            else                ans[j][l]=(cnt+prefixsum(SA[i],SA[i]+height[i],SA[i]+height[i]+qu[ptr].K-las-1))%P;            ptr++;        }        cnt=(cnt+prefixsum(SA[i],SA[i]+height[i],n-1))%P;    }    for (int i=1;i<=m;i++)        ans[i][0]=((ans[i][2]-ans[i][1])%P+P)%P;}int main(){    freopen("hashsum.in","r",stdin);    freopen("hashsum.out","w",stdout);    scanf("%s",s);    n=strlen(s);    scanf("%d",&m);    for (int i=1;i<=m;i++)    {        scanf("%lld%lld",&q[i][0],&q[i][1]);        qu[(i<<1)-1].K=q[i][0]-1,qu[i<<1].K=q[i][1];        qu[(i<<1)-1].id=i,qu[i<<1].id=i;    }    sort(qu+1,qu+1+(m<<1));    pre();    DA();    get_height();    calc();    for (int i=1;i<=m;i++)        printf("%d\n",ans[i][0]);    fclose(stdin);    fclose(stdout);    return 0;}
0 0