hdu 3973 AC's String 字符串哈希处理 查询子串是否是模式串 可以修改

本文介绍了一种利用哈希和树状数组实现的字符串匹配算法,该算法能在给定的一系列单词中查找并验证长字符串S的子串是否存在。此外,还支持对S进行字符更新操作,使得查询过程即使在字符串发生变化后仍能高效完成。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

//


#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
using namespace std;
const int maxn=110000;
//字符串HASH 重复概率很小
//Hash(s)=sigma((s[i]-'a'+1)*27^(i))%mod;
const int mod=1000000007;
int bit[maxn+10];
map<int,bool> g;//使用前要清空
void initHashStr()
{
    bit[0]=1;
    for(int i=1;i<=maxn;i++)bit[i]=((long long)bit[i-1]*27)%mod;
}
//返回true 表示已经存在,false 表示还没有此串,可以添加
bool getHashStr(char* str)
{
    int cnt=0;
    for(int i=0;str[i];i++)
    {
        cnt=((long long)cnt+(long long)(str[i]-'a'+1)*bit[i])%mod;
    }
    cnt=((long long)cnt*bit[maxn])%mod;//important
    if(g[cnt]) return true;
    g[cnt]=true;
    return false;
}
//查询子串是否是模式串 可以修改
//树状数组 sum[i]=sigma((s[i]-'a'+1)*bit[i])%mod;
//子串 hash[s]=(sum[i]-sum[j-1])/bit[j];
//这里将/bit[j]都乘以bit[maxn],可以在字符串hash中先初始化
char str[maxn+1];
int len;
int c[maxn+10];
int lowbit(int x){return x&(-x);}
void add(int x,int val)
{
    for(int i=x;i<=len;i+=lowbit(i))
    {
        c[i]=(((long long)c[i]+val)%mod+mod)%mod;//important 防止出现负数
    }
}
int getsum(int x)
{
    int cnt=0;
    for(int i=x;i>=1;i-=lowbit(i))
    {
        cnt=((long long)cnt+c[i])%mod;
    }
    return cnt;
}
int main()
{
    //freopen("in","r",stdin);
    //freopen("out.txt","w",stdout);
    initHashStr();
    int ci,pl=1;scanf("%d",&ci);
    while(ci--)
    {
        g.clear();
        int n;scanf("%d",&n);
        for(int i=0;i<n;i++)
        {
            scanf("%s",str);//模式串
            getHashStr(str);//对模式串hash
        }
        scanf("%s",str);//母串
        len=strlen(str);
        memset(c,0,sizeof(c));
        for(int i=0;str[i];i++)
        {
            int cnt=((long long)(str[i]-'a'+1)*bit[i])%mod;
            add(i+1,cnt);
        }
        printf("Case #%d:\n",pl++);
        int q;scanf("%d",&q);
        //树状数组从1开始
        while(q--)
        {
            char ch[2],cr[2];int x,y;
            scanf("%s",ch);
            if(ch[0]=='Q')
            {
                scanf("%d%d",&x,&y);//查询[x,y]是否出现在模式串中
                int cnt=(((long long)getsum(y+1)-getsum(x))%mod+mod)%mod;
                cnt=((long long)cnt*bit[maxn-x])%mod;
                if(g[cnt]) printf("Yes\n");
                else printf("No\n");
            }
            else
            {
                scanf("%d%s",&x,cr);
                int cnt=((long long)(cr[0]-str[x])*bit[x])%mod;
                add(x+1,cnt);
                str[x]=cr[0];
            }
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值