一、字符串哈希
1. 什么是哈希
哈希算法是:通过哈希函数将字符串、较大的数等转换为能够用变量表示的或者是直接作为数组下标的数,通过哈希算法转换到的值,称之为哈希值。哈希值可以实现快速查找和匹配。
比如:用数组下标计数法,统计一个字符串,每种字母出现的次数就是一个简单的哈希,将每个字母都映射为了对应的 A s c i i 码。
2. 如何构造哈希
原理:将字符串中的每一个字母都看作是一个数字(例如:从 a - z,视为 1 - 26 );将字符串视为一个是 b 进制的数。(注意:不能映射为 0,因为如果a 为 0,那么 a、aa、aaa 的值将都为 0)
比如:可以将字符串 s = " abcd "视为 26 进制的整数,则可以计算出:hash(s)= 1 * 26 的立方 + 2 * 26 的平方 + 3 * 26 的一次方 + 26 的 0 次方,如果字符串很长,h ( s )很容易超出 long long 范围内。
选取两个合适的互质常数 b 和 h ( b < h ),其中h要尽可能的大一点,为了降低冲突(不同字符串计算到同一个哈希值)的概率。
一般来说:b 取 131 或 13331,h 取 ,最终产生的哈希值的冲突的概率极低。
假设字符串 C=c1c2c3...cm,定义哈希函数:
3. 滚动哈希优化
如果针对一个很长的字符串,判断其中两个长度为 len 的子串是否相同,如果采用O( len )的时间复杂度计算出对应的子串 hash,那和直接取出子串比较的时间复杂度并无差异,因此我们需要使用滚动哈希优化的技巧,可以在O( 1 )的时间复杂度下取出子串的 hash 值。
(1)滚动计算到前缀哈希 h ( k )
设:h ( k )为字符串的子串的哈希值,(先不考虑取 MOD ):
类比 10 进制理解该公式,比如 10 进制的 12345,取出前 3 个数123,如果要去前 4 个数,可以使用:123 * 10(进制)+ 4()= 1234 的方法来取出。
(2)利用前缀哈希 H ( k ) 计算区间哈希值。
设:
因此,求 L ~ R 之间的哈希值:
由上述公式可知,只需要预处理出 ,就可以在O(1)的时间内求得任意子串的哈希值。
(3)时间复杂度
综上,如果在一个长度为 n 的字符串中,任意取长度为 m 的子串进行匹配,时间复杂度为O(n+m)。
二、哈希表
1. 哈希表原理
(1)使用数组下标直接标记元素
哈希表(也叫散列表):是一种高效的、通过把关键码值映射到表中的一个位置来访问记录的数据结构。
类似字符串,查找的时间复杂度是常数时间,缺点是:需要消耗更多的内存。
现在要存储和使用下面的线性表:。
为了用O(1) 的时间实现查找,可以开一个一维数组 A[ 1 ... 13491 ],使得 A[ key ] = key,但显然造成了空间上的很大浪费,尤其是数据范围很大时。
(2)除余法构造哈希值
为了使空间开销减少,我们可以使用第二种方法加以优化,设计一个哈希函数 H(key)=key mod 17,然后令 A[ H ( key ) ] = key,这样一来定义一个一维数组 A[0 ... 16]就以足够,这种方法就是哈希表。
但是这样会造成哈希冲突,比如H(2)和H(19)的值都是 2.
这里与字符串 Hash 有所不同,可能不论我们怎样选用哈希函数,还是很难避免产生冲突。
因此我们考虑对每一个哈希值记一个链表(其实也就相当于邻接表),把所有等于同一个哈希值的数字都存储下来,而查询只要遍历对应的链表即可,这样实际复杂度取决于查询的链表长度,也可以看做是常数级。
例如:我们定义哈希函数H( x ) = x mod 16,对数组进行哈希运算后,插入一些数据的效果如下图:
(3)哈希函数的构造
取余法构造哈希:H(key)= key % b,为了避免冲突,一般为能够存储下来并且尽量大的素数(一般情况下我们根据空间取 10 ~ 6 左右的素数)。一般地说如果 b 的约数越多,那么冲突的几率也就越大。
2. 使用数组模拟邻接表
(1)插入关键操作:
v = hash(x);//计算 x 的哈希值
idx++;
e[idx]=x;
ne[idx]=h[v];
h[v]=idx;
(2)查找关键操作:
int v = hash(x);//计算 x 的哈希值
//循环链表
for(int i=h[v];i!=0;i=ne[i]){if(e[i]==x){return 1;}
}
例如:读入整数 2 5 6 8 3 11,假设 h[ x ] = x % 6。
则:每个元素的哈希值是 2 5 0 2 3 5。
存储数组如下:
element 数组:按读入的顺序,存储每个元素的值。
next 数组:next [ i ]存储和element [ i ]哈希值相同的上一个数的编号。
三、离散化
1. 什么是离散化
离散化是在不改变数据相对大小的条件下,对数据进行相应的缩小,例如:
原数据:1,999,100000,15:处理后:1,3,4,2;
原数据:{100,200},{20,50000},{1,400};
处理后:{3,4},{2,6},{1,5};
离散化本质上可以看成是一种特殊的哈希,其保证数据在哈希以后仍然保持原来的全偏序关系,用于解决:影响最终结果的只有元素之间的相对大小关系,这一类的问题。
2. 离散化的步骤
离散化存在的步骤:
(1)数组中有重复的元素,因此需要去重复;
(2)计算出数组中每个值a [ i ]离散化后的值是多少:二分:
离散化数组:
离散化 vector:
最后
制作不易,点个赞吧!!!
制作不易,点个赞吧!!!
制作不易,点个赞吧!!!