HDU - 3973 AC's String(Hash+线段树)

http://acm.hdu.edu.cn/showproblem.php?pid=3973

题意

给一个词典和一个主串。有两种操作,查询主串某个区间,问这主串区间中包含多少词典中的词语。修改主串某一位置的字符。

分析

题目要求区间查询,和单点更新,那么最先想到的应该是线段树。可字符串怎么利用线段树呢?用hash!首先将词典按规则hash后放入map,然后将主串的hash值放置入线段树中。问题来了,怎么更新结点的hash值呢?假如现在我们知道了s[l...mid]和s[mid+1...r]的hash值,我需要s[l...r]的hash值,根据我的hash规则(将主串看作是p进制数,左边为高位,p为素数),hash[l...r]=hash[l...mid]*p^(r-mid)+hash[mid+1...r]。另外,被输入卡了好久。。不停RE。需要使用更准确的输入格式,我猜测可能是输入数据之间的空格不定导致的。

#include<iostream>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
#include<cstdio>
#include<algorithm>
#include<map>
#include<set>
#define rep(i,e) for(int i=0;i<(e);i++)
#define rep1(i,e) for(int i=1;i<=(e);i++)
#define repx(i,x,e) for(int i=(x);i<=(e);i++)
#define X first
#define Y second
#define PB push_back
#define MP make_pair
#define mset(var,val) memset(var,val,sizeof(var))
#define scd(a) scanf("%d",&a)
#define scdd(a,b) scanf("%d%d",&a,&b)
#define scddd(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define pd(a) printf("%d\n",a)
#define scl(a) scanf("%lld",&a)
#define scll(a,b) scanf("%lld%lld",&a,&b)
#define sclll(a,b,c) scanf("%lld%lld%lld",&a,&b,&c)
#define IOS ios::sync_with_stdio(false);cin.tie(0)
#define lc idx<<1
#define rc idx<<1|1
#define lson l,mid,lc
#define rson mid+1,r,rc
using namespace std;
typedef long long ll;
template <class T>
void test(T a){cout<<a<<endl;}
template <class T,class T2>
void test(T a,T2 b){cout<<a<<" "<<b<<endl;}
template <class T,class T2,class T3>
void test(T a,T2 b,T3 c){cout<<a<<" "<<b<<" "<<c<<endl;}
const int N = 1e6+10;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3fll;
const ll mod = 1000000007;
int T;
void testcase(){
    printf("Case #%d:\n",++T);
}
const int MAXN = 1e5+10;
const int MAXM = 30;
const int p = 19;

struct node{
    int l,r;
    ll hass;
}tree[MAXN<<2];
char str[2000010];
ll P[MAXN];
map<ll,int> ma;
int n;
void init(){
    P[0]=1;
    for(int i=1;i<MAXN;i++){
        P[i]=p*P[i-1];
    }
}
void Hash(char s[]){
    int len = strlen(s);
    ll res=0;
    for(int i=0;i<len;i++){
        res +=P[len-1-i]*(s[i]-‘a‘+1);
    }
    ma[res]=1;
}
void push_up(int m,int idx){
    tree[idx].hass=tree[lc].hass*P[m]+tree[rc].hass;
}
void build(int l,int r,int idx){
    tree[idx].l=l,tree[idx].r=r;
    if(l==r){
        tree[idx].hass=str[l]-‘a‘+1;
        return;
    }
    int mid = (l+r)>>1;
    build(lson);
    build(rson);
    push_up(r-mid,idx);
}
void update(int pos,int idx){
    if(tree[idx].l==tree[idx].r){
        tree[idx].hass=str[pos]-‘a‘+1;
        return;
    }
    int mid = (tree[idx].l+tree[idx].r)>>1;
    if(pos<=mid) update(pos,lc);
    else update(pos,rc);
    push_up(tree[idx].r-mid,idx);
}
ll query(int l,int r,int idx){
    if(tree[idx].l>=l&&tree[idx].r<=r)
        return tree[idx].hass;
    int mid = (tree[idx].l+tree[idx].r)>>1;
    if(r<=mid) return query(l,r,lc);
    else if(l>mid) return query(l,r,rc);
    return query(l,mid,lc)*P[r-mid]+query(mid+1,r,rc);
}

int main() {
#ifdef LOCAL
    freopen("in.txt","r",stdin);
#endif // LOCAL
    int t,m;
    scd(t);
    init();
    T=0;
    char s1[10],s2[10];
    while(t--){
        testcase();
        ma.clear();
        scd(n);
        for(int i=0;i<n;i++){
            scanf("%s",str);
            Hash(str);
        }
        scanf("%s",str);
        int len = strlen(str);
        build(0,len-1,1);
        scd(m);
        while(m--){
            scanf("%s",s1);
            if(s1[0]==‘Q‘){
                int l,r;
                scdd(l,r);
                if(ma.find(query(l,r,1))!=ma.end()) puts("Yes");
                else puts("No");
            }else{
                int pos;
                scanf("%d%s",&pos,s2);
                str[pos]=s2[0];
                update(pos,1);
            }
        }
    }
    return 0;
}

HDU - 3973 AC's String(Hash+线段树)

原文地址:https://www.cnblogs.com/fht-litost/p/9287198.html

时间: 2024-12-14 15:12:01

HDU - 3973 AC's String(Hash+线段树)的相关文章

HDU 3973 AC&#39;s String 字符串哈希

HDU 3973 通过哈希函数将一个字符串转化为一个整数,通过特定的方式可以使得这个哈希值几乎没有冲突(这就是关键之处,几乎没有视为没有= =!, 其实也可以考虑实现哈希冲突时的处理,只是这道题没必要而已),然后使用线段树维护修改后的哈希值. 因为输入的字符串只有26个,考虑使用一个大于等于26的素数p作为进制,然后将原串转化为一个p进制的数mod 2^63(也相当于自然溢出),然后这个数存入map中,然后使用线段树维护长串区间的哈希值,hash[l, r]表示将区间[l, r]的字符串转化为p

HDU 3973 AC&#39;s String (substr 强行匹配)

AC's String Time Limit: 30000/10000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1127    Accepted Submission(s): 316 Problem Description You are given some words {Wi}. Then our stupid AC will give you a very long

hdu 1394 Minimum Inversion Number(线段树)

Minimum Inversion Number Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 10853    Accepted Submission(s): 6676 Problem Description The inversion number of a given number sequence a1, a2, ..., a

HDU 4902 (牛叉的线段树)

Nice boat Problem Description There is an old country and the king fell in love with a devil. The devil always asks the king to do some crazy things. Although the king used to be wise and beloved by his people. Now he is just like a boy in love and c

HDU 4893 Wow! Such Sequence! 水线段树

思路: 线段树走起.. 写完这题就退役T^T 单点更新的时候直接找到这个点的最近fib,然后维护当前和 和 fib的和 #include<stdio.h> #include<string.h> #include<iostream> #include<math.h> #include<algorithm> #include<queue> #include<map> #include<set> #include&l

bnu36907 Subpalindromes 字符串hash+线段树

bnu36907 Subpalindromes 字符串hash+线段树 题意:给一个字符串(<=1e5), 进行操作和查询(<=1e5). 1)将指定位置的字符改为c 2)询问l-r的子串,是否是回文串. 解法 :区间维护pl和pr,表示从左到右的hash和从右到左的hash,然后在up和query中合并区间,最后判断pl和pr是否相等即可. #include <cstdio> #include <ctime> #include <cstdlib> #inc

hdu 1754:I Hate It(线段树,入门题,RMQ问题)

I Hate It Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 33726    Accepted Submission(s): 13266 Problem Description 很多学校流行一种比较的习惯.老师们很喜欢询问,从某某到某某当中,分数最高的是多少.这让很多学生很反感.不管你喜不喜欢,现在需要你做的是,就是按照老师的要求

hdu 4122 Alice&#39;s mooncake shop (线段树)

题目大意: 一个月饼店每个小时做出月饼的花费不一样. 储存起来要钱,最多存多久.问你把所有订单做完的最少花费. 思路分析: ans = segma( num[]*(cost[] + (i-j)*s) ) 整理一下会发现式子就是 cost[]-j*s + i*s 对于每一个订单,我们把i拿出来分析 所以也就用cost - j*s 建树. 然后在储存期间找到最小的花费就行了. #include <cstdio> #include <iostream> #include <algo

HDU 4902 Nice boat 成段线段树

操作1 的时候标记deng[rt]表示下面一段数都是与当前节点的值相同 下次操作2时直接对有deng标记的节点gcd更新 (可能还可以更简单) #include <stdio.h> #include <string.h> #include <stdlib.h> #include <limits.h> #include <malloc.h> #include <ctype.h> #include <math.h> #incl