字符串hash思想的来源(个人猜测):
对于一系列的字符串,如果我们需要重复比较它们是否相同的话,显然这会是一个接近于O(N^3)的算法,时间复杂度太高了。于是我们期望得到一个更好的算法。
把目光着眼到我们的十进制数字,也就是我们平时使用的数字上面。我们很容易发现,比较两个数字的时间复杂度是O(1)的。而数字本质上也是一种字符串,也就是说数字的比较了我们一种启示。
举个例子,假如说给出一个数字构成的字符串,那么我们只需要把这个字符串转化成一个数字,就可以通过O(1)的时间比较这两个字符串是否相同。当然,这个说法是错误的,当数字构成的字符串长度非常大的时候,就算存在这样一种方法可以储存数字,比较两个数字是否相同所需要花费的代价肯定会随着字符串的加长而增大。
回到字符串上面。假设接下来只讨论字符串中只包含小写的英文字母,那么一个字符串实际上可以看成是一个26进制的数字(事实上只要进制大等于于26就可以取得相当好的理论效果)。当然就像我们前面说的,这个“数字”无法储存。但是考虑一下我们一般处理的小规模问题,字符串的数量可能大概也就在1e3到1e5之间,也就是说假如用一个数字代表一个字符串,那么就连int型的整数用来储存这些数字都绰绰有余。而作为一个事实,我们无法储存一个字符串对应的完整的26进制。但是假如我们可以让这个字符串的末8位几乎不重复(实际上一般来说是在一个同余系中不重复,末八位是一个特例),就可以起到一个数字代表一个唯一字符串的效果,并且可以在O(1)时间内完成一对较长的字符串是否相同的对比。
上面的思想所反映出来的字符串比较做法就是字符串hash。一个字符串对应的数字就叫做这个字符串的hash值。显然,就像上面提到的,有可能出现两个字符串对应的hash值相同的情况。为了避免这样的情况,人们往往会把同余系的模数取成一个很大的质数,并且把hash值的值域放大到long long,甚至还有双hash(即一个字符串有两个在不同参数下得到的hash值,当两个字符串的这两个hash值一模一样的时候才认为这两个字符串相同)这样的做法。
算法适用范围:
hash适用于已知字符串集(字符串不一定要完全确定,如果它在确定范围内发生变化,当然可也以认为字符串是已知的),需要大量比较字符串是否相同时。通过线性时间预处理,就可以用O(1)的时间判断任意两个字符串是否相同。
算法实现:
个人的一般做法,hash值定义为long long型,模数取1e15+7,把字符串看成29进制(质数越多越好)。
给出一段代码(默认字符串中只有小写字母):

1 typedef long long LL; 2 const int maxn=10005; 3 const LL mo=1e15+7; 4 5 LL gethash(char *s){ 6 int n=strlen(s); LL tmp=1,re=0; 7 for(int i=0;i<n;i++){ 8 re=(re+tmp*(s[i]-'a'))%mo; 9 tmp=tmp*29%mo; 10 } 11 return re; 12 }