题目链接:http://icpc.njust.edu.cn/Problem/Hdu/3973/
题意是:给出一个模式串,再给出一些串组成一个集合,操作分为两种,一种是替换模式串中的一个字符,还有一种是查询模式串中[l,r]区间的字符串有没有出现在字符串集合中。
由于数据量很大,只能用O(nlogn)复杂度的算法才能通过,我们首先想到区间查询的操作线段树是可以做的,但是怎么样将一个子串唯一化呢?这就要说道字符串哈希了,我的做法是通过字符串哈希将字符串变成31进制数并且让它自然溢出,也就是对2^64取模。然后我们想到如何合并左右子区间呢?根据哈希的思想我们很容易想到:如果右区间的hash值为hash1,左区间的hash值为hash2,右区间的长度是len,则合并之后的hash=hash2*31^len+hash1,根据这样的策略可以知道任何区间的子串的hash值。
代码如下:
1 #include<bits/stdc++.h>
2 using namespace std;
3 typedef unsigned int ui;
4 typedef long long ll;
5 typedef unsigned long long ull;
6 #define pf printf
7 #define mem(a,b) memset(a,b,sizeof(a))
8 #define prime1 1e9+7
9 #define prime2 1e9+9
10 #define pi 3.14159265
11 #define lson l,mid,rt<<1
12 #define rson mid+1,r,rt<<1|1
13 #define scand(x) scanf("%llf",&x)
14 #define f(i,a,b) for(int i=a;i<=b;i++)
15 #define scan(a) scanf("%d",&a)
16 #define dbg(args) cout<<#args<<":"<<args<<endl;
17 #define pb(i) push_back(i)
18 #define ppb(x) pop_back(x)
19 #define inf 0x3f3f3f3f
20 #define maxn 100010
21 #define maxm 2000005
22 int n,m,x,y;
23 ll pp=31;
24 ll t[maxn<<2],p[maxm];
25 char s[maxm],a[maxm];
26 void init()
27 {
28 p[0]=1;
29 f(i,1,maxm-1)
30 {
31 p[i]=p[i-1]*pp;
32 }
33 }
34 ll gethash(char* s)//获取字符串的哈希值
35 {
36 ll ans=0;
37 f(i,0,strlen(s)-1)
38 {
39 ans=ans*pp+s[i]-'a'+1;//哈希值自然溢出,也就是对2^64取模
40 }
41 return ans;
42 }
43 void pushup(int rt,int len)//传入根节点以及右子区间的长度
44 {
45 t[rt]=t[rt<<1]*p[len]+t[rt<<1|1];
46 }
47 void build(int l,int r,int rt)
48 {
49 if(l==r)
50 {
51 t[rt]=a[l]-'a'+1;
52 return;
53 }
54 int mid=l+r>>1;
55 build(lson);
56 build(rson);
57 pushup(rt,r-mid);
58 }
59 void update(int l,int r,int rt,int pos,char C)
60 {
61 if(l==r)
62 {
63 t[rt]=C-'a'+1;
64 return;
65 }
66 int mid=l+r>>1;
67 if(pos<=mid)update(lson,pos,C);
68 else update(rson,pos,C);
69 pushup(rt,r-mid);
70 }
71 ll query(int l,int r,int rt,int L,int R)
72 {
73 if(l>=L&&r<=R)//只有区间完全重合的时候取出hash值时不需要另加操作
74 {
75 return t[rt];
76 }
77 int mid=l+r>>1;
78 if(R<=mid) return query(lson,L,R);//分成三种情况,因为合并的时候需要乘系数
79 else if(L>mid) return query(rson,L,R);
80 return query(lson,L,mid)*p[R-mid]+query(rson,mid+1,R);
81 }
82 int main()
83 {
84 //freopen("input.txt","r",stdin);
85 //freopen("output.txt","w",stdout);
86 std::ios::sync_with_stdio(false);
87 int tt;
88 scan(tt);
89 init();
90 f(kk,1,tt)
91 {
92 set<ll> map;//字符串到hash值的映射表
93 scan(n);
94 f(i,1,n)
95 {
96 scanf("%s",s);
97 map.insert(gethash(s));
98 }
99 scanf("%s",a+1);
100 int len=strlen(a+1);
101 build(1,len,1);
102 scan(m);
103 char q[3];
104 pf("Case #%d:\n",kk);
105 while(m--)
106 {
107
108 scanf(" %s",q);
109 if(q[0]=='Q')
110 {
111 scan(x);
112 scan(y);
113 x++;//注意代码中线段树的左端点是从1开始的
114 y++;
115 // dbg(query(1,len,1,x,y));
116 if(map.find(query(1,len,1,x,y))!=map.end())
117 pf("Yes\n");
118 else pf("No\n");
119 }
120 else if(q[0]=='C')
121 {
122 char str[3];
123 scan(x);
124 x++;
125 scanf("%s",str);
126 update(1,len,1,x,str[0]);
127 }
128 }
129 }
130 }
来源:https://www.cnblogs.com/randy-lo/p/12444091.html