算法 {字符串哈希}
字符串哈希
定义
字符串str="abc"
(a
為低位) 他的哈希值為A*(Base^2) + B*Base + C
(在模Mod
下) 其中ABC
為abc
對應的Base
進制數;
性质
""
空字符串, 他的哈希值 应该设置为0
; (他是没有正规定义的 只是我们自己人为的去规定他 实践发现 他可以设置为0
);
@DELI;
這個哈希算法是絕對的 只與當前字符串有關 與其他背景都無關;
比如S="abcdef"
他的子字符串bcd
的哈希值 你通過{1:預處理S 來獲得子串[1:3]的哈希值; 2:直接求bcd
的哈希值} 兩者是一樣的! 都等於B*Base^2 + C*Base + D
;
@DELI;
映射从1开始
假如是从0开始映射, 则abc 和 bc, 他们的对应的P进制是: (0) (1) (2) 和 (1) (2)
如果采用(前高位)的哈希顺序, 则他们对应的10进制值都是: (1 * P + 2), 就冲突了;
其原因在于: 两者的长度不同; 如果长度相同, 不会出现: 前导零的情况;
一般, 最好都从(1)开始映射
@DELIMITER
字符串的哈希冲突
由于字符串哈希, 不像整数哈希是有探测法(即整数哈希 不会出现哈希冲突, 探测法解决了这点)
而字符串哈希没有探测法, 因此, 字符串哈希是会发生冲突的; 假如他发生冲突, 目前是没有解决办法的!
只能去尝试更换: 字符串哈希的取模值: 将(默认的1e9+7) 改为: (1e9 + 21)
算法
模板
namespace ___HashString{
//< 哈希算法: 字符串`str="abc"`(`a`為低位) 他的哈希值為`A*(Base^2) + B*Base + C`(在模`Mod`下) 其中`ABC`為`abc`對應的`Base`進制數, 即*字符串低位 其實是高量級*!
constexpr int __HashRadix_ = 131; // [131, 13331, 524287]
using __HashModularType_ = ___Supimo_Algorithm::Modular<int64_t, int64_t(1e18)+7>; // [1e9+7, 1e9+21, 2147483647, 1e18+7, 9223372036854775807];
constexpr int __StringMaximalLength = ?; // 字符串的最大長度
int Get_charHash( char _c){ // 返回值 必須是在`[1, __HashRadix_)`的範圍
int ANS;
if( _c>='a' && _c<='z'){ ANS = 1 + _c - 'a';} // [1, 26]
else if( _c>='A' && _c<='Z'){ ANS = 27 + _c - 'A';} // [27, 52]
else if( _c>='0' && _c<='9'){ ANS = 53 + _c - '0';} // [53, 62]
else{ ASSERT_SYSTEM_(0);}
return ANS;
}
__HashModularType_ Get_stringHash( const char * _str, int _length){
__HashModularType_ ANS = 0;
for( int i = 0; i < _length; ++i){
if( i == 0){ ANS = Get_charHash(_str[i]);}
else{ ANS *= __HashRadix_; ANS += Get_charHash(_str[i]);}
}
return ANS;
}
__HashModularType_ Get_powerOfBase( int _power){ // `Base ^ power (%Mod)`
ASSERT_SYSTEM_( _power>=0 && _power<__StringMaximalLength);
static __HashModularType_ ANS[ __StringMaximalLength]; static bool __isFirst = true;
if( __isFirst){
__isFirst = false;
for( int i = 0; i < __StringMaximalLength; ++i){
if( i == 0){ ANS[ i] = 1;}
else{ ANS[i] = ANS[i-1] * __HashRadix_;}
}
}
return ANS[ _power];
}
__HashModularType_ Get_mergedStringHash( const __HashModularType_ & _pre, const __HashModularType_ & _suf, int _sufLen){
// 比如`P="ab", S="xyz"`, 則`_sufLen==3` `pre,suf`為`P,S`的哈希值, 則字符串"abxyz"的哈希值 為返回值;
return _pre * Get_powerOfBase(_sufLen) + _suf;
}
class ___Calc_SubStringsHash{ // 預處理子字符串, 即`O(1)`的獲得一個字符串的任意*子串*的哈希值
public:
vector<__HashModularType_> __PrefixSum;
int __Length;
void Initialize( const char * _str, int _length){
__PrefixSum.resize(_length); __Length = _length;
for( int i = 0; i < __Length; ++i){
if( i==0){ __PrefixSum[i] = Get_charHash( _str[i]);}
else{ __PrefixSum[i] = __PrefixSum[i-1] * __HashRadix_ + Get_charHash( _str[ i]);}
}
}
__HashModularType_ Get_hash_subString( int _l, int _r){
ASSERT_SYSTEM_( _l>=0 && _l<=_r && _r<__Length);
__HashModularType_ ANS = __PrefixSum[ _r]; if( _l != 0){ ANS -= (__PrefixSum[_l-1] * Get_powerOfBase(_r-_l+1));}
return ANS;
}
}; // class ___Calc_SubStringsHash
} // namespace ___HashString
@DELI;
如果哈希冲突了, 将1e9 + 7
改为1e9 + 21
;
@DELI;
這個int Mod_
, 你不能把他設置成一個long long
的數, 這就錯誤了, 因為會導致溢出 (兩個long long
的乘積 溢出);
.
只有說 你把Mod_
給去掉 改為對unsigned long long
自然溢出, 此時 溢出是正確的 (因為溢出 就相當於對2^64
取模);
应用
@LINK: https://editor.csdn.net/md/?not_checkout=1&articleId=137013594
;
使用大哈希, 即int64_t
;
@DLEI;
@LINK: https://editor.csdn.net/md/?not_checkout=1&articleId=132818193
;
獲取字符串的前後綴拼接的哈希值
對於S: "abcdef"
, 獲取其(後綴+前綴)的字符串的哈希值;
比如要得到efabcd
這個字符串的哈希值 令暴力算法O(N)得到的結果為H
(即E*Base^n + F*... + D
), 而實際上 你可以通過O(1)獲得他的哈希值: 令A:S.Get_hash(4,5); B:S.Get_hash(0,3)
, 即A為ef
的哈希值(E*Base + F
) B為abcd
的哈希值(A*Base^3 + ... + D
), 則efabcd
的哈希值為 A * Power[4] + B == H
;
筆記
原理: 两个不同的P进制数, 他对应的十进制数, 是不同的;
假如P = 26, 两个P进制数: (a1) (a2) (a3) 和 (b1) (b2) (b3)
如果存在: ai != bi, 那么其对应的10进制数, 即(a1 * P^2 + a2 * P + a3)是不同的
这就像是, 假如P为熟悉的十进制, 那么(012) 和 (022)是不同的;
因此, 对于一个P进制数(也可以看做是一个P进制字符串), 他所对应的十进制数, 就称为他的(哈希值)
基于此, 假如一个字符串有P个不同的字符, 则对应一个P进制, 每个字符映射到[0, P)之中
由于其对应的十进制数, 会爆出LL, 需要使用取模操作;
双哈希
定义
一般的字符串哈希, 即获得一个MOD h
的哈希值; 但他如果发生了哈希冲突的话, 此时有2种解决办法:
0: 扩大MOD
的取模数的范围; 比如原来是1e9 + 7
, 现在改成1e18 + 7
;
1: 使用双哈希, 即两个取模数 一个是1e9 + 7
一个是1e9 + 21
, 此时 哈希值就是一个pair< MOD1, MOD2>
(注意此时Radix=131
这是不变的)