zoukankan      html  css  js  c++  java
  • 详解HASH(字符串哈希)

    HASH意为(散列),是OI的常用算法。

    我们常用哈希的原因是,hash可以快速(一般来说是O(段长))的求出一个子段的hash值,然后就可以快速的判断两个串是否相同。

    今天先讲string类的hash。


    可以发现,与一个string有关的HASH值不仅仅跟每个字符的个数有关,还和字符的位子有关。

    通过简单的思考,我们可以构造如图的模型:

    写一个比较正常的hash模板吧

    const int EE = 97;
    const int MOD = 100000007;
    int HASH(string p)
    {
       int E = 1;
       int ret = 0;
       int tl = p.size();
       for (int i=0;i<tl;i++)
          ret += E*p[i], E *= EE;
       return ret;
    }
    题目来了:

    KMP问题

    题目描述

    如题,给出两个字符串s1和s2,其中s2为s1的子串,求出s2在s1中所有出现的位置。

    输入输出格式

    输入格式:

    第一行为一个字符串,即为s1

    第二行为一个字符串,即为s2

    输出格式:

    一行包含一个整数,表示s2在s1中出现的位置的个数

    输入输出样例

    输入样例#1:
    ABABABC
    ABA
    输出样例#1:
    2
    

    说明

    时空限制:1000ms,128M

    数据规模:

    设s1长度为N,s2长度为M

    对于30%的数据:N<=15,M<=5

    对于70%的数据:N<=10000,M<=100

    对于100%的数据:N<=1000000,M<=1000000


     思路

     首先说明:此题正解为KMP,不为hash。如果想知道KMP算法,请百度一下。

    但是我们学的可是“hash”呀,不能直接预处理,如果直接预处理的话,时间为O(n*m),炸掉。

    我们就可以递推:

      "已知长度为m的序列a[1]...a[m],现在已知"a[1]...a[m]"的hash值为K,欲求a[2]...a[m+1]的hash值。"

    我首先想到的是乘法逆元,但还有其他的更简便的方法。

    可以这一样想:"改变EE的赋值方式,反过来赋值,这样的话可以直接删去第一个'a[1]*EE^(m-1)',再乘一个'EE',往后再移一位,再加上一个a[m+1]."

    那么,转移方程也很容易写了,为HASH[i]=(HASH[i-1]-a[i-2]*E[1]%M+M)%M*EE%M+a[i-2+m];(HASH[i]表示a[i-1]到a[i+m-2]的hash值。

    另附代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    int n,k,len1,len2;
    int next1[1000001];
    char s1[1000001];
    char s2[1000001];
    long long HASH[1000001];
    long long E[1000001],M=1234567898765;

    long long EE = 97;

    int init()
    {
        long long Key=0;
        int ans=0;
        memset(E,0,sizeof(E));
        memset(HASH,0,sizeof(HASH));
        E[len2]=1;
        for (int i=len2-1;i>=1;i--)
            E[i]=E[i+1]*EE%M;
        for (int i=1;i<=len2;i++)
            HASH[1]=(HASH[1]+E[i]*(s1[i-1]))%M;
        for (int i=1;i<=len2;i++)
            Key=(Key+E[i]*(s2[i-1]))%M;
        if (HASH[1]==Key) ans++;
        for (int i=2;i<=len1-len2+1;i++)
        {
            HASH[i]=(HASH[i-1]-s1[i-2]*E[1]%M+M)%M*EE%M+s1[i-2+len2];
            if (HASH[i]==Key) ans++;
        }
        printf("%d ",ans);
    }
    int main(){
        scanf("%s",s1) ;
        scanf("%s",s2) ;
        len1=strlen(s1);
        len2=strlen(s2);
        init();
        return 0;
    }
  • 相关阅读:
    [转]K/3加密控制规则
    修改Delphi2009的界面风格
    [转]软件版本号讲解: 什么是Alpha, Beta, RC
    百度程序题目连续数问题
    得到正整数a的16进制表示
    四舍五入至某小数位后返回数字串
    返回相同宽度数字型字符串
    百度程序题目连续数问题 另解
    求二进制表示中1的个数
    六支筷子取其二,恰为一双的概率
  • 原文地址:https://www.cnblogs.com/dgklr/p/8485659.html
Copyright © 2011-2022 走看看