一、前言:
有些数据不经处理是难以利用的。所谓哈希,就是通过哈希函数将这种难以简单利用的数据(比如矩阵、字符串等等)转化为可以用一个变量表示甚至可以作为数组下标的哈希值。有了哈希值,就可以实现时间复杂度近乎为常数的快速查找与匹配,更简单有效地利用一些复杂数据。
二、字符串哈希:
即对象为字符串的哈希。常将字符串看做一个不严格的b进制的数(有时数位上的数有可能要比b还要大),转换为10机制后再模一个数p(以便存储)得到哈希值。即定义哈希函数:
H(C)=(c1*b^(m-1) + c2*b^(m-2) + . . . + c0*b^0)% p
(c为字符串,m为字符串长度)
当两字符串的哈希值相同时,我们就认为这两个字符串是一模一样的。显然这样的判断有时会有“冲突”,即相同哈希值的两个字符串可能会不同。而冲突的概率显然与p和b有关。一般来说,p的因数越多,冲突概率越大(故p一般选大素数)。
做子串匹配时,我们要知道所有子串的哈希值。难道要用O(n^2)的复杂度全部处理出来吗?这里有一个叫做滚动哈希的优化技巧,实质是用了前缀和与递推的思想,同时我们可以用unsigned int 型,用自然溢出时对2^32取模来代替手写取模:
设H(C,k)为字符串c前 k 个字符构成的字符串的哈希值,发现H(C,k+1)= H(C,k) * b+c[k+1]);
主串上某一子串c[k]c[k+1]...c[k+len]的哈希值即=H(C,k+len) - H(C,k-1) * b^n;
这样只要预处理出b的次幂再用O(n)的时间处理出H(C,1)...H(C,m),主串所有子串的哈希值也就知道了。
平时常应用与各种字符串匹配问题。为了进一步降低冲突概率,还可以再另处理一组哈希值,称为“双哈希”,此时取模的素数常用1e9+7和1e9+9,这两个孪生素数可以将随机数据的冲突概率降到1/(1000000009*1000000007)(当然还是防不了毒瘤出题人故意出一个卡哈希的数据,OIer与出题人的斗智斗勇永不停息......)
三、哈希表:
运用哈希可以将大的数据转化为较小的哈希值,同时发现如果哈希函数选取合适,那么相同哈希值的不同元素会非常少。由此提醒我们可以搞一个可以快速查询元素的结构——哈希表:
在一个相同哈希值的域下记录相应数据。在一个哈希表中查询数据x时,算出x的哈希值h=H(x),将h域下所有的元素与x进行比较(不会很多),得出结果。
通常用邻接链表实现哈希表,代码这里不写出了,蓝书70页写的很详细。代码中H变量没提到值,应该等于b,注意一下插入数据时先判个重。
最后有一个小技巧:如果真实数据过大,域下记录的数据可以是模另一个素数的余数,达到“双哈希”的效果。
原文地址:https://www.cnblogs.com/InductiveSorting-QYF/p/11219223.html