hdu 3973(hash+线段树)

AC's String

Time Limit: 30000/10000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)


Problem Description
You are given some words {Wi}. Then our stupid AC will give you a very long string S. AC is stupid and always wants to know whether one substring from S exists in the given words {Wi} .

For example, S = "abcd", and the given words {Wi} = {"bc", "ad", "dd"}. Then Only S[2..3] = "bc" exists in the given words. (In this problem, the first element of S has the index "0".)

However, this is toooooooooooo easy for acmers ! The stupid and evil AC will now change some letters in S. So could you solve this problem now?
 

Input
The first line is one integer T indicates the number of the test cases. (T <=20)

Then for every case, there is one integer n in the first line indicates the number of the given words(The size of the {Wi}) . Then n lines has one string which only has 'a'- 'z'. (1 <= n <= 10000, sigma|Wi| <= 2000000) .

Then one line has one string S, here |S| <= 100000.

Then one integer m, indicating the number of operations. (1 <= m <= 100000)

Then m lines , each line is the operation:

(1)Q L R , tell AC whether the S[L..R] exists in the given strings ;

(2)C X Y , chang S[X] to Y, here Y : 'a'-'z' .
 

Output
First output “Case #idx:” in a single line, here idx is the case number count from 1.Then for each "Q" operation, output "Yes" if S[L..R] exists in the given strings, otherwise output "No".
 

Sample Input
  
  
1 2 ab ioc ipcad 6 Q 0 2 Q 3 4 C 1 o C 4 b Q 0 2 Q 3 4
 

Sample Output
  
  
Case #1: No No Yes Yes
 
解题思路:这个题目最开始的想法是把S[l....r]串存入线段树节点中,但是题目给的数据量太大,一下就爆了。。。所以参考了别人的,把这段字符串的哈希值存入。。
诶,,看来线段树的运用相当灵活。。。此外,字符串哈希有一个性质我没想到,所以我一直在想怎么样把每个节点中的哈希值更新。。。感觉这道题又让自己对字符串哈希的理解又深入了一点。。。
我们假设已经知道了S[l....mid]和S[mid+1......r]两个字符串的哈希值(按照我的习惯是逆着算的),我们要求S[l......r]的哈希值。。hash[l......r] = hash[l......mid]+hash[mid+1....r]*p^(mid-l+1),p是质数。。。其实这个性质很容易推理的,只要按照计算哈希值的定义就可以推理了:hash[l.......len-1] = s[len-1]*p^(len-1-l)+s[len-2]*p^(len-1-l+1)+.......+s[l]*p^0。

AC:
#include<cstdio>
#include<cstring>
#include<map>
#define maxn 100010
#define p 31
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=len-1;i>=0;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+tree[id<<1|1].hashes*hh[mid+1-s];
}
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+tree[id<<1|1].hashes*hh[mid+1-tree[id].l];
}
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)+query(mid+1,t,id<<1|1)*hh[mid+1-s];
}
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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值