hdu3973 AC's String 线段树+字符串hash
Posted randy-lo
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了hdu3973 AC's String 线段树+字符串hash相关的知识,希望对你有一定的参考价值。
题目链接: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: ",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 "); 118 else pf("No "); 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 }
以上是关于hdu3973 AC's String 线段树+字符串hash的主要内容,如果未能解决你的问题,请参考以下文章
AC日记——Aragorn's Story HDU 3966
HDU 3341 Lost's revenge(AC自动机+状压DP)