hdu 3973 AC's String(字符串hash+线段树单点修改)

题意:

题意:给出10000个模式串和一个长度为100000的匹配串,有两种操作:
1.查询匹配串的子串[L,R]是否存在于模式串中
2.修改匹配串某个位置的字符

解析:

将一个字符串看成是一个P进制的数字,那么可以知道每一个字符串都可以被唯一表示( P>256 ,并不考虑高精度)。可是由于字符串的长度比较大,那么我们无法保存如此大的数字。于是使用Hash mod2^64来作为Hash。(的却可能会冲突,可是冲突的概率趋于无穷小)
Hash(s,P)=s[0]PLen1+s[1]PLen2++s[Len1]P0mod264
P随便选个素数什么的都可以。
之后假设没有修改操作,那么我们可以与处理出 H[i] 表示 S[0..i] 的Hash
那么为了获得S[l..r]的Hash,我们有:
S[0..r]=s[0]Pr+s[1]Pr1++s[r]P0
S[0..l1]=s[0]Pl1+s[1]Pl2++s[l1]P0
于是
S[l..r]=s[l]Prl+s[l+1]Prl1++s[r]P0
=S[0..r]S[0..l1]Prl+1
=H[r]H[l1]Prl+1
其中 Prl+1 显然直接预处理。
于是查询就可以O(1)
现在需要修改某些字符
于是可以使用一颗线段树来维护
[L..R]维护S[L..R]的Hash,这样更新查询均解决。更新和查询均为O(logN)级别

注意:

query的时候要更改查询的区间,因为这个错误debug了好久。

my code

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
#define ls (o<<1)
#define rs (o<<1|1)
#define lson ls, L, M
#define rson rs, M+1, R
#define sigma_size 33
using namespace std;
typedef unsigned long long ll;
const int N = (int)2e6 + 10;
const int maxn = (int)1e5 + 10;
set<ll> mySet;

char str[N];
ll sumv[maxn<<2], H[maxn];

ll idx(char c) { return c - 'a' + 1; }

ll getHash(char *s) {
    int len = strlen(s);
    ll ret = 0;
    for(int i = 0; i < len; i++) {
        ret = ret * sigma_size + idx(s[i]);
    }
    return ret;
}

inline void pushUp(int o, int L, int R) {
    int M = (L + R) / 2;
    sumv[o] = H[R - M] * sumv[ls] + sumv[rs];
}

void init() {
    H[0] = 1;
    for(int i = 1; i < maxn; i++) {
        H[i] = H[i-1] * sigma_size;
    }
}

void build(int o, int L, int R) {
    if(L == R) {
        sumv[o] = idx(str[L]);
        return ;
    }
    int M = (L + R) / 2;
    build(lson);
    build(rson);
    pushUp(o, L, R);
}

ll query(int o, int L, int R, int ql, int qr) {
    if(ql <= L && R <= qr)
        return sumv[o];
    int M = (L + R) >> 1;
    if(qr <= M) return query(lson, ql, qr);
    else if(ql > M) return query(rson, ql, qr);
    return query(lson, ql, M) * H[qr - M] + query(rson, M+1, qr);
}

void modify(int o, int L, int R, int pos) {
    if(L == R) {
        sumv[o] = idx(str[pos]);
        return ;
    }
    int M = (L + R) >> 1;
    if(pos <= M) modify(lson, pos);
    else modify(rson, pos);
    pushUp(o, L, R);
}

int n, q;
int main() {
    init();
    int T, cas = 1;
    scanf("%d", &T);
    while(T--) {
        mySet.clear();

        scanf("%d", &n);
        for(int i = 0; i < n; i++) {
            scanf("%s", str);
            mySet.insert(getHash(str));
        }

        scanf("%s", str);
        int len = strlen(str);
        build(1, 0, len-1);

        char oper[5], c[5];
        int ql, qr, pos;

        printf("Case #%d:\n", cas++);
        scanf("%d", &q);
        for(int i = 1; i <= q; i++) {
            scanf("%s", oper);
            if(oper[0] == 'Q') {
                scanf("%d%d", &ql, &qr);
                ll ret = query(1, 0, len-1, ql, qr);
                puts(mySet.count(ret) ? "Yes" : "No");
            }else {
                scanf("%d%s", &pos, c);
                str[pos] = c[0];
                modify(1, 0, len-1, pos);
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值