Hash(哈希)——解决字符串问题

关于字符串Hash:把字符串有效转化为一个整数

把每个字符串有效地、唯一的映射到每个"不同"的整数,就可以很好地处理字符串;

找到一个Hash函数,使得每一个字符串都能够映射到一个整数上;

hash[i] = ( hash[i-1] * p + idx(s[i]) )%mod;

字符串:abc, bbc, aba, aadaabac    字符串下标从0开始,  先把a映射为1, b映射为2,c为3,d为4;

即 idx(a) = 1,  idx(b) = 2,  idx(c) = 3,  idx(d) = 4; //  idx 为字符串的字符的编号;

假设 p = 13, mod = 101; 先把abc映射为一个整数, 

hash[0] = 1, // 表示a的映射为1;

hash[1] = ( hash[0]*p+idx(b) )%mod = ( 1*13+2 )%101 =15; //表示ab的映射为15;

hash[2] = ( hash[1]*p+idx(c) )%mod = ( 15*13+3)%101 = 97; //表示abc的映射为97;


用同样的方法,我们可以把 bbc,aba,aadaabac 都映射为整数;

用同样的hash函数,得到如下结果:

abc ~ 97, bbc ~ 64, aba ~ 95, aadaabac ~ 35;

可以发现这是一个 字符串到整数的映射;

这样,我们就可以记录下每个字符串对应的整数,当下一次出现了一个已经出现过的字符串时,查询整数是否出现过,就可以知道字符串是否重复出现

一、现在要判断两个字符串是否一致,

直接用他们的hash值判断即可,如果hash一致,就认为字符串一致, 如果hash值不一致,就认为是不同的字符串;

如果判断两个字符串是否一致,不用麻烦,直接先判断长度是否一致,然后判断每个对应的字符是否一致即可;

二、但是,如果要判断多个字符串里有多少个不同的字符串,

① 两两字符串都进行比较(时间复杂度太高)

②把每个字符串hash成一个整数,然后把所有整数进行去重操作。

对于刚才这个式子hash[i] = ( hash[i-1] * p + idx(s[i]) )%mod;  我们刚才取得p = 13, mod = 101;, 实际上可能会有冲突,就是说,两个不同的字符串,映射到了一个整数上;

而解决方法是:想办法调整p和mod,使冲突的概率减小之又小,p取一个较大的素数,mod取一个更较大的素数;习惯上,p取一个6到8位的素数即可,mod一般取大素数1e9+7(1000000007) 或 1e9(1000000009); 

mod取一个小的数与mod取一个大的数相比,冲突的概率就会更大,所以取大才能降低冲突的概率;

三、如何求一个子串的hash值

在之前,我们求出了hash[i], 表示第i个前缀和的hash值,

hash [ i ] = ( hash [ i-1 ] * p + idx ( s[ i ] ) )%mod  表示的是第i个前缀的hash值,是一个hash的前缀和;

如果要求S[ l...r ]这个子串的hash值:

hash[ l...r ] = (hash[ r ] - hash[ l-1 ]*( p^(r-l+1) ))%mod;

hash[ l...r ] 如果有负数, 即当hash[ l...r ] < 0 时, hash[ l...r ] = hash[ l...r ] +mod 即可,这样就可以保证每个子串的hash值在[ 0,mod-1 ] 的范围内,准确的用hash值来处理字符串;

四、常用的几个字符串hash法

1、unsifgned long long hash [n];   hash[i] = hash[i-1]*p (自动取模)

2、hash[i] = (hash[i-1]*p + idx(s[i]) )%mod

3、双hash

hash1[ i ] = (hash1[i-1]*p + idx(s[i]) )%mod1;

hash2[ i ] = (hash2[i-1]*p + idx(s[i]) )%mod2;   pair表示一个字符串;

hash法一:安全指数(三星)

unsigned long long hash[N];

定义一个unsigned long long类型的变量, 它的范围是在[0. 2^64]内,这就相当于,当数超过2^64-1后,他就会溢出!相当于一个数模 2^64的过程。

那么hash函数可以理解为:hash[ i ] =  (hash[i-1]*p) %(2^64);   p取一个大素数,一般习惯取 1e9+7或1e9+9;

hash法二:安全指数(四星)

这个之前提过了,

hash1[ i ] = (hash1[i-1]*p + idx(s[i]) )%mod1;  p取一个6到8位的素数, mod取一个大素数,一般取到1e9+7或1e9+9;

hash法三:安全指数(五星)

double hash(双哈希)

即取两个mod值,mod1和mod2

hash1[ i ] = (hash1[i-1]*p + idx(s[i]) )%mod1;

hash2[ i ] = (hash2[i-1]*p + idx(s[i]) )%mod2; 

mod1一般取为1e9+7 ,mod2一般取为1e9+9    (因为1000000007与1000000009是一对孪生素数,取他们,冲突的概率极低)

 整理自:https://www.bilibili.com/video/av7230433?from=search&seid=9265921605754549673

补充说明:求子串的hash值

如果一个字符串 s = "1234" , 子串是 t = "34" (下标从1开始),取  p = 10。  // p == 10, 用来类比, p一般都取较大素数;

依次得到 s 的哈希值,  

hash[1] = 1, hash[2] = 12, hash[3] = 123, hash[4] = 1234.

然后 t 的哈希值为  34    // t 直接算总的哈希值就行了。

 t 的哈希值   34 = hash[4] - hash[2] * 100.. ,100 也就是 10^(t的长度)   这样就算出来 s 串中 "34" 的哈希值。

你可能感兴趣的:(数据结构训练)