字符串哈希是个很好玩的东西
什么是字符串哈希?
字符串哈希嘛……没什么好解释的。通常来说就是把一个字符串映射成一个数字,并且尽量使映射的结果不相互冲突。
但是哈希的姿势应该是怎样的呢?
据我了解,OI选手们的hash姿势大多是不尽相同。最简单的就是把每一位乘积取模作为hash值。
嗯,不乏有好多高级的姿势。
ELFhash
解释:首先我们的hash结果是一个unsigned int类型的数据:0000 0000 0000 00001.hash左移4位,将str插入(一个char有八位)这里我开始也一直是怀疑的态度,那么第一个字节的高四位不就乱了吗实际上这也是我们的第一次杂糅,我们是故意这么做的,这里我们需要注意标记一下,我们在第一个字节的高四位做了第一次杂糅2.x这里用0xf0000000获取了hash的第四个字节的高四位,并用高四位作为掩码做第二次杂糅在这里我们首先声明一下,因为我们的ELFhash强调的是每个字符都要对最后的结构有影响,所以说我们左移到一定程度是会吞掉最高的四位的,所以说我们要将最高的四位先对串产生影响,再让他被吞掉,之后的所有的影响都是叠加的,这就是多次的杂糅保证散列均匀,防止出现冲突的大量出现3.x掩码右移24位移动到刚才的5-8位哪里在对5-8位进行第二次杂糅4.我们定时清空高四位,实际上这部操作我们完全没有必要,但是算法要求,因为我们下一次的左移会自动吞掉这四位//这里存疑,不会减少我们的hash的范围?5.str递增,引入下一个字符进行杂糅6.返回一个缺失了最高符号位的无符号数(为了之后防止用到了有符号的时候造成的溢出)作为最后的hash值
一些有趣的哈希题
luoguP3370 【模板】字符串哈希
题目描述
如题,给定N个字符串(第i个字符串长度为Mi,字符串内包含数字、大小写字母,大小写敏感),请求出N个字符串中共有多少个不同的字符串。
输入输出格式
输入格式:
第一行包含一个整数N,为字符串的个数。
接下来N行每行包含一个字符串,为所提供的字符串。
输出格式:
输出包含一行,包含一个整数,为不同的字符串个数。
良心的哈希模板题。
我写了个双哈希:ELF+笨蛋hash
1 #include<cstdio> 2 #include<set> 3 #include<hash.h> 4 5 int n,sum; 6 std::set<std::pair<int, int> > f; 7 8 unsigned int ELFhash(char *str) 9 { 10 unsigned int hash=0; 11 unsigned int x=0; 12 while(*str) 13 { 14 hash=(hash<<4)+*str; 15 if((x=hash & 0xf0000000)!=0) 16 { 17 hash^=(x>>24); 18 hash&=~x; 19 } 20 str++; 21 } 22 return (hash & 0x7fffffff); 23 } 24 int BLINDhash(char *str) 25 { 26 int hash = 1; 27 int MO = 100019977; 28 while (*str) 29 { 30 hash = ((hash**str)+3627)%MO; 31 str++; 32 } 33 return hash; 34 } 35 int main() 36 { 37 char ch[10003]; 38 scanf("%d",&n); 39 for (int i=1; i<=n; i++) 40 { 41 scanf("%s",ch); 42 int xx = ELFhash(ch), yy = BLINDhash(ch); 43 if (!f.count(std::make_pair(xx, yy))){ 44 f.insert(std::make_pair(xx, yy)); 45 sum++; 46 } 47 } 48 printf("%d ",sum); 49 return 0; 50 }
bzoj3578: GTY的人类基因组计划2
Description
GTY召唤了n个人来做实验,GTY家的房子很大,有m个房间一开始所有人都在1号房间里,GTY会命令某人去某个房间等待做实验,或者命令一段区间的房间开始实验,实验会获得一些实验信息点数,点数为房间里的人数,如果一个房间里的一群人已经做过实验了那么这些人将不会增加实验信息点数(不会增加是针对这一群人的,不是对这群人中的每个人,即1,2,3做了实验,1,2再做实验还会增加2点实验点数)
Input
第一行两个整数n,m,q(n,m,q<=10^5)表示人数,房间数和操作数
接下来q行每行一个操作 "C i j"表示让第i个人去房间j "W l r" 表示让区间[l,r]的房间做实验
Output
对于每一个W操作,输出一个数,表示此次操作所获得的实验点数
HINT
善用STL
题目分析
又是一道标算被hash搞爆的题……
RIP出题人。
仔细看一看出题人写的这句:“在下方注明了:善用STL……,于是map<set<int>,bool> 即可……呃……”,嗯很明显这里可以用hash来取代set,再用map<int,bool>判一判就好啦!
槽点
这个hash姿势很重要啊!!!注意在给每一个人赋标号的时候,尽量要使标号的范围覆盖至long long范围,否则就很容易冲突!
1 #include<bits/stdc++.h> 2 const int maxn = 100003; 3 4 std::set<int> legalRoom; 5 std::map<long long, bool> mp; 6 long long info[maxn],num[maxn],tag[maxn],lb[maxn]; 7 int n,m,q; 8 9 int main() 10 { 11 scanf("%d%d%d",&n,&m,&q); 12 srand(3627); 13 for (int i=1; i<=n; i++) {lb[i] = 1ll*rand()*rand()*rand()%1000000000*(1ll*rand()*rand()*rand()%2000000000),tag[i] = 1;info[1] ^= lb[i];} 14 num[1] = n; 15 legalRoom.insert(1); 16 for (int i=1; i<=q; i++) 17 { 18 char ch = getchar(); 19 while (ch!='C'&&ch!='W') ch = getchar(); 20 int x,y; 21 scanf("%d%d",&x,&y); 22 if (ch=='C'){ 23 if (tag[x]==y) continue; 24 legalRoom.erase(y),legalRoom.erase(tag[x]); 25 info[tag[x]] ^= lb[x]; 26 num[tag[x]]--; 27 if (!mp[info[tag[x]]]) legalRoom.insert(tag[x]); 28 info[y] ^= lb[x]; 29 num[y]++; 30 if (!mp[info[y]]) legalRoom.insert(y); 31 tag[x] = y; 32 }else{ 33 int ans = 0; 34 std::set<int>::iterator it; 35 for (it = std::lower_bound(legalRoom.begin(), legalRoom.end(), x); *it<=y&&it!=legalRoom.end(); it=std::lower_bound(legalRoom.begin(), legalRoom.end(), x)) 36 { 37 mp[info[*it]] = 1; 38 ans += num[*it]; 39 legalRoom.erase(it); 40 } 41 printf("%d ",ans); 42 } 43 } 44 return 0; 45 }
【不定期更新】