算法 {字符串哈希,双哈希}

算法 {字符串哈希}

字符串哈希

定义

字符串str="abc"(a為低位) 他的哈希值為A*(Base^2) + B*Base + C(在模Mod下) 其中ABCabc對應的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这是不变的)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值