浅谈字符串哈希

一、引入

        哈希算法是通过一个哈希函数H,将一种数据(如字符串)转化为另一种数据(通常转化为整形数值),有些题可用map做,但数据一大就要用到字符串哈希

二、字符串哈希

        寻找长度为n的主串S中的匹配串T(长度为m)出现的位置或次数属于字符串匹配问题。朴素算法(或称为暴力)就是枚举所有子串的起始位置,每枚举一次就要使用O(m)的时间,总共要O(nm)的时间。当然字符串匹配可以用KMP做,但这里介绍一下字符串哈希。

        字符串哈希就是将每个字符串转化为一个数值,然后遍历主串,判断在主串起始位置为i长度为m的字符串的哈希值与匹配串的哈希值是否相等即可,每次判断为O(1)的时间。这样就可以转化为O(n)的时间完成判断。那么问题来了,怎么预处理哈希值呢?

        我们选用两个互质常数base和mod,假设匹配串T=abcdefg……z(注意这里不是指T只有26位)那么哈希值为              H(T)=(a*base^(m-1)+b*base^(m-2)+c*base^(m-3)+……+z)%mod。相当于把每个字符串转换为一个base进制数,所以对于每道题我们取base时,要大于每一位上的值(避免重复),例如我们用的十进制数每一位都是小于10的。

例如字符串C="ABDB",则H(C)=‘A’+'B'*base+'D'*base^2+'B'*base^3(本人习惯直接取字符askII码值,也可以使‘A’=1)

         那么怎么判断主串起始位置为i长度为m的字符串的哈希值与匹配串的哈希值是否相等呢?这里有个公式,若求字符串中第i位到第j位的哈希值(i。有了这个公式,我们可以预处理一个数组H[i]表示字符串从第一位到第i位的哈希值和数组power[i]表示base^i。加上判断的时间,总时间为O(n+m)。

        在计算时,我们可以使用无符号类型(通常本人习惯使用unsigned long long)的自然溢出,这样就可以不用%mod,包括减法也方便许多。

        当然哈希会有可能重复,base值越大重复可能性越小,本人通常取131或233317。也可使用双哈希,即两个不同的mod

那么举个栗子玩玩:Power Strings(Poj2406)

【问题描述】

      给定若干个长度小于等于1 000 000的字符串,询问每个字符串最多由多少个相同的子串重复连接而成。如:ababab最多由3个ab连接而成。

【输入格式】

若干行,每行一个字符串,遇“.”结束

【输出格式】

每行一个数,表示最多由多少子串连成

【样例输入】

    abcd

    aaaa

    ababab

    .

【样例输出】

    1

    4

    3

【题目解析】

外循环枚举子串长度,如果整除总长度,那么内循环判断,代码如下:

#include 
#include 
#include 
#include 

using namespace std;

typedef unsigned long long ull;
const int N=1e6+5;
const ull prime=233317;//表示base 
ull power[N]={1},h[N]; 

int main()
{
    int l,i,j;
    string a;
    for(i=1;i>a)
    {
        if(a==".")
            break;
        l=a.size();
        h[0]=ull(a[0]);
        for(i=1;i

参考文献:黄新军等《信息学奥赛一本通·提高篇》

你可能感兴趣的:(浅谈字符串哈希)