字符散列(hash)

字符串hash的核心思想与整数hash是一致的,将字符串映射成为一个整数唯一表示。

假设字符串只由A~Z组成,将A~Z看做0-25,这样就转化成了一个26进制数,再将这个26进制数转化为10进制数,则每个字符串都能用一个整数唯一表示。

H[i] = H[i-1] * 26 + index(str[i])

index将字符转化为对应的数字,H[i]表示字符串的前i个字符的hash值。

代码如下:

int hashchar(char a[], int len){
    int sum = 0;
    for(int i = 0; i < len; ++i){
        sum = sum * 26 + (a[i] - 'A');
    }
    return sum;
}

考虑到整数范围,这里需注意len不能过大。若字符中出现小写字母,则将a~z看做26-51,这样就变成了52进制数,道理同上;

int hashchar(char a[], int len){
    int sum = 0;
    for(int i = 0; i < len; ++i){
        if(a[i] >= 'A' && a[i] <= 'Z')
        sum = sum * 52 + (a[i] - 'A');
        else
        sum = sum * 52 + (a[i] - 'a') + 26;
    }
    return sum;
}

出现数字同理,但这里有个小技巧,如果知道,数字出现的位置固定则直接加上去,例如BCD4,讲前面的字符转化为731,然后直接在末尾加上4变成7314即可。

进阶篇

你应该注意到,当字符串很长时,即使sum为long long 类型仍然会溢出。这时我们采用取模的方法。如下列的散列函数:

H[i] = (H[i-1] * 26 + index(str[i]))%mod

通过这种方式把字符串转化成范围上能够接受的整数,但是这会引起另外一个问题,可能会有多个字符串的hash值相同,产生冲突,不过,在实践中发现,在int范围内,如果把进制数设置成一个10^7级别的素数(如 10000019),同时吧mod设置为一个10^9级别的素数(如 1000000007)。如下,这样做冲突的概率极低,很难产生冲突。

H[i] = (H[i-1] * p + index(str[i]))%mod

猜你喜欢

转载自blog.csdn.net/qq_41807801/article/details/82533052
今日推荐