虽然已经有映射函数map,而且其适用范围还更广,但在字符串上还是哈希更好理解~~(哈希在密码学上很重要,抱有某种目的同学们注意危险言论了)~~
学过高中生物的同学们应该对胞间连丝印象很深,hash和map就是一种信息学胞间连丝,很形象。
hash是一种对应法则,将字符串按照这种法则运算,所得的权值有可能会有重复(下文称为冲突),所以我们运算字符串的权值时会采用其他进制,根据题意需具体分析。FOR INSTANCE,若只会出现小写字母则31进制足以撑场,但如果是大小写字母都有,那就要用131进制比较合适了。
模板如下:
const int base=131;
inline int hash(string c){
int len=c.size();
int ret=0;
for(int i=0;i<=len-1;i++){
ret*=base,ret+=c[i]-'A';
}
return ret;
}
这里的base就是进制了,具体情况具体分析。
好了,那我们如何储存字符串信息呢?
这里要用链表了,也就是哈希表。
因为我们计算完哈希值后,要表明这个字符串被加进了字典中,按朴素思想应该直接flag[hash(s)]=1,但这样数组flag会开的很大,根本存不下,所以我们对哈希值采用取模(模数不要太大,我比较喜欢19997),优先选择质数~~,比如19260817这时有同学就要疑惑了,这不明显会出问题吗? 比如1%19997=1,19998%19997==1,不就会冲突了吗?
嘿嘿,这时链表就有用了。
我们开一个结构体,储存两个信息:val和nt;
nt很明显是存下一个节点的,而val则用来储存字符串的原hash值。
当我们算出hash(s)==v时,设t=v%mod,就在t的后面拉一条链,串起来。
代码:
struct node{
int nt,val;
}e[200018];
inline void update(string s){
int x=hash(s);
cnt++;
e[cnt].nt=head[x%mod];
e[cnt].val=x;
head[x%mod]=cnt;
}
当我们查找字典中是否存在字符串s时,先计算出其哈希值x,如果存在,那么它一定会被保存在x%mod后面,我们只需在x%mod后面的那条链里扫一遍,查看是否有一个点权值为x就OK了。
代码:
inline int ask(string ch){
int flag=0;
int x=hash(ch);
for(int i=head[x%mod];i;i=e[i].nt){
if(e[i].val==x){
flag=1;
}
}
return flag;
}
}
ps:head[k]数组存的是k号拉链后面的第一个点在e数组中的编号(但实际上这个点是最后一个插入此链的)