Hash

Hash

  以前做普及试炼场的$Hash$部分是拿$Map$水过的...后来觉得还是应该学一下,发现比$Map$的用处多多啦!

  Hash有很多种,对于字符串一般使用进制$Hash$,就是把字母映射到数字上,再将整个字符串转成一个高进制数比如$233$,再对一个大质数取模,从而将字符串变成一个数字。在实际应用中,有两种需求:

  ·懒得写取模(是有多懒),可以用$unsigned long long$保存$Hash$值,相当于对$2^{64}$取模;

  ·对正确性有着极高的要求:使用两个质数,同时求两个$Hash$值,只有两个值都相等时才判断为相等,这两个质数最好去成比较大又比较接近的两个数,出现$Hash$冲撞的概率就非常低了.一本通上给出了一对很好的双$Hash$模数---孪生质数:$10^9+7,10^9+9$

  最近发现字符串类的题目还是用$char$数组比较方便,不容易出一些奇奇怪怪的错误,下面是使用字符数组做Hash题目的一些常用操作:

  头文件部分: 

  1 # include <cstring>
  2 # include <string>
  3 # define ULL unsigned long long

  对于思想永远僵化在$Pascal$的人群,其实字符数组的下标也是可以从$1$开始用的:

  1 scanf("%s",s+1);
  2 l=strlen(s+1); //求长度 

  计算进制的各个幂,方便之后截取子串以及滚动求字符串的前缀$Hash$值:

  1 po[0]=1;
  2 for (R i=1;i<=maxn;++i)
  3     po[i]=po[i-1]*base;
  4 for (R i=1;i<=l;++i)
  5     hash[i]=hash[i-1]*base+s[i]-'A'+1;

  截取$[l,r]$子串的$Hash$值: 

  1 a=hash[r]-hash[l]*po[r-l+1];

  如果感到难以理解,就把进制换成10来想一想,提交的时候不要忘记改回233就可以了.

  Hash当然还有许多种,比如$Xor-Hash$(个人觉得非常不靠谱),$mod-hash$(虽然也觉得不靠谱,但是一般配合$Hash$表使用,就没有这样的顾虑了),一本通上还介绍了奇妙的将数字乘上一个无理数再取整的方法...

  最终发现还是进制$Hash$最好用。根据生日悖论,出现冲撞的概率非常小,出现冲撞的步长是$\sqrt{mod}$,但是模数非常大以至于几乎可以忽略这个概率.说到生日悖论,暑假$shallwe$来讲课,发现机房$50$多个人竟然没有生日在同一天的23333

  刚刚浏览器挂掉了,然后我已经写完的两道题也跟着消失了,真伤心。

  再来一次:

  POJ 2752:http://poj.org/problem?id=2752

  题意概述:对于给定的串,询问它的哪些前缀同时也是后缀。

  $Hash$模板题。

  
 1 # include <cstdio>
 2 # include <iostream>
 3 # include <cstring>
 4 # include <string>
 5 # define R register int
 6 # define ULL unsigned long long
 7 
 8 using namespace std;
 9 
10 const int base=233;
11 const int maxn=400005;
12 char c[maxn];
13 int l;
14 ULL hash[maxn];
15 ULL po[maxn];
16 
17 int main()
18 {
19     po[0]=1;
20     for (R i=1;i<=maxn;++i)
21         po[i]=po[i-1]*base;
22     while (~scanf("%s",c+1))
23     {
24         l=strlen(c+1);
25            for (R i=1;i<=l;++i)
26             hash[i]=hash[i-1]*base+c[i]-'a'+1;
27         for (R i=1;i<=l;++i)
28             if(hash[i]==hash[l]-hash[l-i]*po[i]) printf("%d ",i);
29         printf("\n");
30     }
31     return 0;
32 }
POJ 2752

 

  POJ 3461:http://poj.org/problem?id=3461

  题意概述:给定两个串,求$A$串在$B$串的哪些位置出现过.

  枚举在$B$中的起点,截取子串进行$Hash$判断.

  
 1 # include <cstdio>
 2 # include <iostream>
 3 # include <cstring>
 4 # include <string>
 5 # define R register int
 6 # define ULL unsigned long long
 7 
 8 using namespace std;
 9 
10 const int base=233;
11 const int maxn=1000005;
12 int l1,l2,ans,T;
13 char w[10005],t[maxn];
14 ULL po[maxn],hash[maxn],goa;
15 
16 int main()
17 {
18     po[0]=1;
19     for (R i=1;i<=maxn;++i)
20         po[i]=po[i-1]*base;
21     scanf("%d",&T);
22     while (T--)
23     {
24         ans=0;
25         scanf("%s",w+1);
26         scanf("%s",t+1);
27         goa=0;
28         l1=strlen(w+1);
29         l2=strlen(t+1);
30         for (R i=1;i<=l2;++i)
31             hash[i]=hash[i-1]*base+t[i]-'A'+1;
32         for (R i=1;i<=l1;++i)
33             goa=goa*base+w[i]-'A'+1;
34         for (R i=1;i<=l2-l1+1;++i)
35             if(hash[i+l1-1]-hash[i-1]*po[l1]==goa) ans++;    
36         printf("%d\n",ans);
37     }    
38     return 0;
39 }
POJ 3461

 

  火星人:https://www.lydsy.com/JudgeOnline/problem.php?id=1014

  题意概述:带修改带插入的LCP问题。$L<=10^5,M<=1.5 \times 10^5$

  SA好像并不能资瓷修改和插入,所以只好想一个更暴力的办法:二分+Hash;如果只有修改,可以用线段树做,但是如果要插入就只能用平衡树了。如果用平衡树维护前缀哈希值,可能就崩溃了,因为平衡树并不能O(1)地求出前驱后继,所以需要另辟蹊径...维护区间哈希值!这个可以通过左右儿子的区间哈希值和子树大小 $O(1)$ 维护,从这里入手就可以解决了。说起来容易做起来难,调了一晚上才调出来,比较开心的是在洛谷上竟然1A了。bz上T掉了,所以进行了一番魔改,后来发现有的地方少Splay几次就玄学的快起来了。

  
  1 # include <cstdio>
  2 # include <iostream>
  3 # include <cstring>
  4 # include <string>
  5 # define R register int
  6 # define ULL unsigned long long
  7 
  8 using namespace std;
  9 
 10 const int maxn=100005;
 11 const int base=233;
 12 char s[maxn],opt[10],cc[4];
 13 ULL po[maxn<<2];
 14 int m,x,y,n,vis[maxn],cnt,l,r,rt,c;
 15 struct node
 16 {
 17     int siz,f,ch[2];
 18     int x;
 19     ULL v;
 20 }t[maxn];
 21 
 22 void update (int x)
 23 {
 24     int l=t[x].ch[0],r=t[x].ch[1];
 25     if(t[l].v==0&&t[r].v==0) 
 26         t[x].v=t[x].x;
 27     else 
 28         if(t[r].v==0) 
 29             t[x].v=t[l].v*base+t[x].x;
 30     else
 31         t[x].v=(t[l].v*base+t[x].x)*po[ t[r].siz ]+t[r].v;
 32     t[x].siz=t[l].siz+t[r].siz+1;
 33 }
 34 
 35 int D (int x) { return t[ t[x].f ].ch[1]==x; }
 36 
 37 void rotate (int x)
 38 {
 39     int f=t[x].f,ff=t[f].f;
 40     int dx=D(x),df=D(f);
 41     int k=t[x].ch[dx^1]; 
 42     t[k].f=f;
 43     t[f].ch[dx]=k;
 44     t[f].f=x;
 45     t[x].ch[dx^1]=f;
 46     t[x].f=ff;
 47     t[ff].ch[df]=x;
 48     update(f);
 49     update(x);
 50 }
 51 
 52 void Splay (int x,int g)
 53 {
 54     while(t[x].f!=g)
 55     {
 56         if(t[ t[x].f ].f==g) rotate(x);
 57         else if(D(x)==D(t[x].f)) rotate(t[x].f),rotate(x);
 58         else rotate(x),rotate(x);
 59     }
 60     update(x);
 61     if(!g) rt=x;
 62 }
 63 
 64 void ins (int l,int r,int v)
 65 {
 66     Splay(l,0),Splay(r,l);
 67     t[r].ch[0]=++cnt;
 68     t[cnt].siz=1;
 69     t[cnt].x=t[cnt].v=v;
 70     t[cnt].f=r;
 71     update(r),update(l);
 72     Splay(cnt,0);
 73 }
 74 
 75 int find (int k)
 76 {
 77     int x=rt;
 78     while (1)
 79     {
 80         int s=t[ t[x].ch[0] ].siz;
 81         if (s>=k) x=t[x].ch[0];
 82         else if(s+1==k) return x;
 83         else x=t[x].ch[1],k-=s+1; 
 84     }
 85 }
 86 
 87 ULL ask (int x)
 88 {
 89     if(x==0) return 0;
 90     int l=find(1),r=find(x+2);
 91     Splay(l,0);
 92     Splay(r,l);
 93     ULL ans=t[ t[r].ch[0] ].v;
 94 //    x=find(x);
 95 //    Splay(x,0); 这两句不加的话好像复杂度不是很对,但是加上后就跑的特慢,所以去掉了 
 96     return ans;
 97 }
 98 
 99 bool check (int a,int b,int k,ULL A,ULL B)
100 {
101     ULL x,y;
102     x=ask(a+k-1)-A*po[k];
103     y=ask(b+k-1)-B*po[k];
104     return (x==y);
105 }
106 
107 int solve (int x,int y)
108 {
109     if(x>y) swap(x,y);
110     int l=1,r=n-y+1,mid,ans=0;
111     ULL A=ask(x-1),B=ask(y-1);
112     while(l<=r)
113     {
114         mid=(l+r)>>1;
115         if(check(x,y,mid,A,B)) ans=mid,l=mid+1;
116         else r=mid-1;
117     }
118     return ans;
119 }
120 
121 int read ()
122 {
123     R x=0;
124     char c=getchar();
125     while (!isdigit(c)) c=getchar();
126     while (isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
127     return x;
128 }
129 
130 int main()
131 {
132     scanf("%s",s+1);
133     n=strlen(s+1);
134     scanf("%d",&m);
135     po[0]=1;
136     for (R i=1;i<=n+m;++i) po[i]=po[i-1]*base;
137     cnt=2;
138     rt=1;
139     t[2].f=1;
140     t[1].ch[1]=2;
141     for (R i=1;i<=n;++i)
142     {
143         l=find(i),r=find(i+1);
144         x=s[i]-'a'+1;
145         ins(l,r,x);
146     }
147     for (R i=1;i<=m;++i)
148     {
149         scanf("%s",opt+1);
150         if(opt[1]=='Q')
151         {
152             x=read(),y=read();
153             printf("%d\n",solve(x,y));
154         }
155         else if(opt[1]=='R')
156         {
157             scanf("%d",&x);
158             scanf("%s",cc+1);
159             c=cc[1]-'a'+1;
160             l=x,r=x+2;
161             l=find(l),r=find(r);
162             Splay(l,0);
163             Splay(r,l);
164             x=t[r].ch[0];
165             t[x].v=t[x].x=c;
166             update(r);
167             update(l);
168             Splay(x,0);
169         }
170         else if(opt[1]=='I')
171         {
172             scanf("%d",&x);
173             scanf("%s",cc+1);
174             c=cc[1]-'a'+1;
175             l=find(x+1);
176             r=find(x+2);
177             Splay(l,0);
178             Splay(r,l);
179             x=t[r].ch[0]=++cnt;
180             t[x].v=t[x].x=c;
181             t[x].siz=1;
182             t[x].f=r;
183             update(r);
184             update(l);
185             Splay(x,0);
186             n++;
187         }
188     }
189     return 0;
190 }
火星人

 

---shzr

posted @ 2018-09-16 21:23  shzr  阅读(240)  评论(0编辑  收藏  举报