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:\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  } 

猜你喜欢

转载自www.cnblogs.com/randy-lo/p/12444091.html