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$模板题。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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 3461:http://poj.org/problem?id=3461
题意概述:给定两个串,求$A$串在$B$串的哪些位置出现过.
枚举在$B$中的起点,截取子串进行$Hash$判断.
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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 }
火星人: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几次就玄学的快起来了。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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