HDU 3973 AC's String

本题使用字符串的Hash来解决。将一个字符串看成是一个P进制的数字,那么可以知道每一个字符串都可以被唯一表示(P> 256, 并不考虑高精度)。可是由于字符串的长度比较大,那么我们无法保存如此大的数字。于是使用Hash mod2^64来作为Hash.(的却可能会冲突,可是冲突的概率趋于无穷小)

然后就是Hash(s,P) = s[0]*P^(Len-1) + s[1] *P^(Len -2) + … + s[Len-1]*P^0 (mod2^64)

P随便选个素数什么的都可以。

之后假设没有修改操作,那么我们可以与处理出H[i]表示S[0..i]Hash

那么为了获得S[l..r]Hash,我们有:

S[0..r]= s[0]*P^r + s[1]*P^(r-1) +…+s[r]*P^0

S[0..l-1]=s[0]*P^(l-1)+s[1]*P^(l-2)+…+s[l-1]*P^0

于是

S[l..r]=s[l]*P^(r-l)+s[l+1]*P^(r-l-1)+…+s[r]*P^0

=S[0..r] – S[0..l-1]*P^(r-l+1)

=H[r] – H[l-1]*P^(r-l+1)

其中P^(r-l+1)显然直接预处理。

于是查询就可以O(1)

现在需要修改某些字符!

于是可以使用一颗线段树来维护

[L..R]维护S[L..R]Hash,这样更新查询均解决。更新和查询均为O(logN)级别


#include<cstdio>
#include<cstring>
#include<map>
#define maxn 100010
#define p 33
using namespace std;
typedef unsigned long long ll;
struct Tree{
    int l,r;
    ll hashes;
}tree[300000];
char str[2000100];
ll hh[maxn];
void init(){
    int i;
    hh[0]=1;
    for(i=1;i<=maxn;i++)
        hh[i]=hh[i-1]*p;
}
ll calhash(){
    int i,len=strlen(str);
    ll sum=0;
    for(i=0;i<len;i++)
        sum=sum*p+str[i]-'a'+1;
    return sum;
}
void build(int s,int t,int id){
    tree[id].l=s;tree[id].r=t;
    if(s==t){
        tree[id].hashes=str[s]-'a'+1;
        return;
    }
    int mid=(s+t)>>1;
    build(s,mid,id<<1);
    build(mid+1,t,id<<1|1);
    tree[id].hashes=tree[id<<1].hashes*hh[t-mid]+tree[id<<1|1].hashes;
}
void update(int l,int id){
    if(tree[id].l==tree[id].r){
        tree[id].hashes=str[l]-'a'+1;
        return ;
    }
    int mid=(tree[id].l+tree[id].r)>>1;
    if(l<=mid) update(l,id<<1);
    else update(l,id<<1|1);
    tree[id].hashes=tree[id<<1].hashes*hh[tree[id].r-mid]+tree[id<<1|1].hashes;
}
ll query(int s,int t,int id){
    if(tree[id].l==s && tree[id].r==t)
        return tree[id].hashes;
    int mid=(tree[id].l+tree[id].r)>>1;
    if(t<=mid) return query(s,t,id<<1);
    else if(s>mid) return query(s,t,id<<1|1);
    return query(s,mid,id<<1)*hh[t-mid]+query(mid+1,t,id<<1|1);
}
int main(){
    int t,T,pos,l,r,i,q,n;
    char s1[10],s2[10];
    map<ll,int>mp;
    init();
    scanf("%d",&T);
    for(t=1;t<=T;t++){
        printf("Case #%d:\n",t);
        scanf("%d",&n);
        mp.clear();
        for(i=1;i<=n;i++){
            scanf("%s",str);
            mp.insert(make_pair(calhash(),1));
        }
        scanf("%s",str);
        int len=strlen(str);
        build(0,len-1,1);
        scanf("%d",&q);
        for(i=1;i<=q;i++){
            scanf("%s",s1);
            if(s1[0]=='C'){
                scanf("%d%s",&pos,s2);
                str[pos]=s2[0];
                update(pos,1);
            }
            else{
                scanf("%d %d",&l,&r);
                if(mp.find(query(l,r,1))!=mp.end()) printf("Yes\n");
                else printf("No\n");
            }
        }
    }
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值