精华内容
下载资源
问答
  • 主要介绍了C#计算字符串哈希值(MD5、SHA)的方法,以实例形式较为详细的分析总结了C#计算字符串哈希值的各种常用技巧,具有一定参考借鉴价值,需要的朋友可以参考下
  • 字符串哈希

    2021-01-07 21:24:11
    何为字符串哈希 所谓字符串哈希,即对一个字符串形成单向加密的过程,使其拥有尽可能独一无二的编号,通过这种低概率的编号重复,使得字符串的匹配尽可能高效。 如何字符串哈希 最普遍的字符串哈希方式,即进制哈希...
  • 用C语言实现常用的字符串哈希函数,比如RSHash、JSHash、PJWHash、FNVHash等
  • 字符串哈希总结

    2019-08-13 12:29:16
    昨天做了学长拉的字符串哈希的专题,今天便来做个总结吧。 哈希是数据结构里面的内容,其实就是把字符串编个号,用数字来表示,这样查询和判断相同时就方便了许多。 但是编的这个号也是有很关键,我们需要尽可能的...

    昨天做了学长拉的字符串哈希的专题,今天便来做个总结吧。

    哈希是数据结构里面的内容,其实就是把字符串编个号,用数字来表示,这样查询和判断相同时就方便了许多。

    但是编的这个号也是有很关键,我们需要尽可能的保证不同字符串的编号不会相等,即冲突概率为0.

    一个关键的定理:哈希值相等的字符串不一定相等,字符串相等的哈希值一定相等。因此我们需要尽可能减少第一种情况。

    在处理子串问题时,我们一般采用前缀和去维护哈希值(当然,你可以使用树状数组啊,平衡树啊,去动态维护整个序列),因为它是比较简单易于实现的哈希函数(当然也有其他哈希函数,详情请参考:字符串哈希函数)

    下面的讲解都是根据前缀和来实现的。


    常用的哈希方法有三种:自然溢出,单哈希和双哈希。

    一:自然溢出

    自然溢出其实就是用unsigned long long来代替了对质数取余这一步骤,unsigned long long本身范围就是2^64-1,也相当与对一个大质数取余了,其实到了一样。不过因为简单易于实现,最常用这种方法来实现。

    先声明hash[maxn]的类型为unsigned long long,哈希函数hash[i]=hash[i-1]*p+s[i]-'a'+1(s为字符串)

    二:单哈希

    单哈希是比较经典的哈希方法,hash[i]=(hash[i-1]*p+s[i]-'a'+1)%mod(p,mod为质数,而且p<mod,p与mod取尽可能大的质数,减少冲突概率)

    三:双哈希

    顾名思义,用两个哈希数组hash1,hash2和两个mod1,mod2来实现,结果用一个二元组表示

    hash1[i]=(hash1[i−1])∗p+s[i]-'a'+1) % mod1

    hash2[i]=(hash2[i−1])∗p+s[i]-'a'+1) % mod2

    hash结果为<hash1[n],hash2[n]>

    (备注:为什么要乘于p,相当于把字符串看成一个p进制的数,类比16进制。哈希值就是进制转换之后对mod取余的结果


    下面讲一个哈希算法的经典用法,就是取出字符串的子串的哈希值。

    先抛出代码!!

    hash[l~r] = hash[1~r] - hash[1~l - 1] * pow(P, r - l + 1); 
    

    看起来这个式子人畜无害,但是对于取模运算要谨慎再谨慎,注意到括号里面是减法,即有可能是负数,故做如下的修正:    hash=((hash[r]−hash[l−1]∗pr−l+1)%MOD+MOD)%MOD;

    至此得到求子串hash值公式。

     

    当然处理的时候,求p的多少次方,我们都会预处理一下,这个代码的思想大家理解一下就应该会懂。

    下面给出证明:(来自网上大佬)

    例子
    假设有一个|S|=5的字符串,设Si为第i个字符,其中1≤i≤5。
    根据定义分别求出hash[i]
    hash[1]=s1
    hash[2]=s1∗p+s2
    hash[3]=s1∗p2+s2∗p+s3
    hash[4]=s1∗p3+s2∗p2+s3∗p+s4 
    hash[5]=s1∗p4+s2∗p3+s3∗p2+s4∗p+s5
    
    现在我们想求s3s4的hash值,不难得出为s3∗p+s4,并且从上面观察,
    如果看hash[4]−hash[2]并将结果种带有s1,s2系数的项全部消掉,就是所求。
    但是由于p的阶数,不能直接消掉,所以问题就转化成,将hash[2]乘一个关于p的系数,
    在做差的时候将多余项消除,从而得到结果。
    
    不难发现,对应项系数只差一个p2,而4 - 3 + 1 = 2(待求hash子串下标相减再加一),
    这样就不难推导出来此例题的求解式子。
    hash[4]−hash[2]∗p^(4−2+1)
    

    关于哈希算法的好的板子,大家可以网上百度一下kuangbin大佬的板子,我感觉挺不错的。

    单独考哈希的题出的很少,而且有的其他方法也可以实现,不过哈希还是很有用的,是其他的基础。

    哈希常常和二分一起出现,后面的题也有。


    下面抛出一些经典的哈希题。

    字符串哈希
    hdu1880——哈希+恶心输入输出题解
    POJ1743——哈希+二分(后缀数组SA)题解
    POJ3461——经典哈希(求可重叠串个数)题解
    POJ2406——经典哈希(求最大循环次数)题解
    POJ2752——经典哈希(公共前缀后缀长度)题解
    SCU4438——经典哈希+前缀和题解
    hdu1496——哈希妙用(暴力+优化)题解

    终于搞完了!!!!

     

     

     

     

     

     

    展开全文
  • 字符串哈希算法简单入门学习

    千次阅读 2019-06-05 00:23:26
    字符串哈希算法 字符串哈希,最著名的就是BKDRHash,也就是将字符串变成数值,并且最后变成的数值是一个P进制的数(一班取131或者13331),一般来说P最好为素数.然后我们之所以需要前缀和,是因为我们这道题目是求一个...

     

    字符串哈希算法

    字符串哈希,最著名的就是BKDRHash,也就是将字符串变成数值,并且最后变成的数值是一个P进制的数(一班取131或者13331),一般来说P最好为素数.然后我们之所以需要前缀和,是因为我们这道题目是求一个区间的字符串,又因为是哈希表,所以我们得求出区间哈希和,又因为是是区间和,所以我们得用前缀和求O(n)预处理,来实现和哈希一般的O(1)常数级别查询.这种Hash常用,且冲突概率极低,offer必备,同时竞赛OI党必备,是优秀的算法,容易精简易理解.

    例题1:AcWing——兔子与兔子

    很久很久以前,森林里住着一群兔子。

    有一天,兔子们想要研究自己的 DNA 序列。

    我们首先选取一个好长好长的 DNA 序列(小兔子是外星生物,DNA 序列可能包含 26 个小写英文字母)。

    然后我们每次选择两个区间,询问如果用两个区间里的 DNA 序列分别生产出来两只兔子,这两个兔子是否一模一样。

    注意两个兔子一模一样只可能是他们的 DNA 序列一模一样。

    输入格式

    第一行输入一个 DNA 字符串 S。

    第二行一个数字 m,表示 m 次询问。

    接下来 m 行,每行四个数字 l1,r1,l2,r2l1,r1,l2,r2,分别表示此次询问的两个区间,注意字符串的位置从1开始编号。

    输出格式

    对于每次询问,输出一行表示结果。

    如果两只兔子完全相同输出 Yes,否则输出 No(注意大小写)。

    数据范围

    1≤length(S),m≤10000001≤length(S),m≤1000000
    输入样例:

    aabbaabb
    3
    1 3 5 7
    1 3 6 8
    1 2 1 2
    输出样例:

    Yes
    No
    Yes

    #include<bits/stdc++.h>
    using namespace std;
    typedef unsigned long long ULL;
    const int N=1000010,base=131;//进制
    char str[N];
    ULL h[N],p[N];              //h数组是求前缀哈希值,p数组是求进制的i次幂
    
    ULL get(int l,int r){      //求一段区间的哈希值
        return h[r]-h[l-1]*p[r-l+1];
    }
    
    int main(){
        scanf("%s",str+1);
        int n=strlen(str+1);   //从str[1]开始存
        p[0]=1;
        for(int i=1;i<=n;i++){
            h[i]=h[i-1]*base+str[i]-'a'+1;  //利用前缀和的思想求哈希值
            p[i]=p[i-1]*base;
        }
       int m;
       cin>>m;
       while(m--){
           int l,r,x,y;
           scanf("%d%d%d%d",&l,&r,&x,&y);
           if(get(l,r)==get(x,y))
           cout<<"Yes"<<endl;
           else
           cout<<"No"<<endl;
       }
        
        return 0;
    }

    例题2:139. 回文子串的最大长度

    如果一个字符串正着读和倒着读是一样的,则称它是回文的。

    给定一个长度为N的字符串S,求他的最长回文子串的长度是多少。

    输入格式

    输入将包含最多30个测试用例,每个测试用例占一行,以最多1000000个小写字符的形式给出。

    输入以一个以字符串“END”(不包括引号)开头的行表示输入终止。

    输出格式

    对于输入中的每个测试用例,输出测试用例编号和最大回文子串的长度(参考样例格式)。

    每个输出占一行。

    输入样例:

    abcbabcbabcba
    abacacbaaaab
    END
    

     输出样例:

    Case 1: 13
    Case 2: 6

    前缀和+后缀和+二分+Hash(哈希) O(nlogn)
    我们发现0这道题目数据范围极其恐怖,那么只有一个办法可以让我们求解这道题目,那就是哈希,或者是O(n)复杂度的Manacher算法,但是我们这道题目是锻炼我们的哈希水平,所以我们这里只说如何用哈希算法求解.作者目前还不会马拉车算法,作者先去学一下再来补

    上一道兔子兔子兔子的题目,我们知道判断两个字符串是否相等,可以使用字符串哈希,也就是将字符串算成P进制数值,然后区间和判断即可,那么这道题目我们需要一个正的字符串,还需要一个反的字符串,然后如果正字符串等于反的字符串,那么奇数回文串就2+1,偶数回文串就直接2即可.之所以要这么做,因为我们是要回文对不对,我们需要将回文拆解成为一个正字符串和一个反字符串,这样才好处理这道题目.

    既然如此,我们可以算出一个前缀和,再算出一个后缀和,然后就可以知道,正字符串和一个反字符串.字符串的哈希值就是这个区间的哈希值和.

    算完之后,我们当前就只需要枚举一个mid中间点,因为所有回文串都是有一个中间点(奇),或者中间区间(偶),然后二分分别寻找这个字符串长度即可,记住不是回文串,回文串的长度,是字符串长度* 2 + 1(奇) 或者是字符串长度 * 2(偶数).

    切记如果说这个最大回文串为1(也就是所有字符都不一样,比如说abcdefg),那么输出是1,不是3,奇数回文串=奇数字符串*2+1,你们要小心特判这种情况,或者处理二分边界.

    #include <bits/stdc++.h>
    using namespace std;
    #define ull unsigned long long
    #define fir(i,a,b) for(int i=a;i<=b;i++)
    #define fic(i,a,b) for(int i=a;i>=b;i--)
    #define Mod 131 //P进制
    const int N=1000007;
    char s[N];
    ull f1[N],f2[N],p[N];
    int ans,t,l,r,mid;
    ull Hash1(int i,int j)//正字符串的哈希值
    {
        return (f1[j]-f1[i-1]*p[j-i+1]);
    }
    ull Hash2(int i,int j)//反字符串的哈希值
    {
        return (f2[i]-f2[j+1]*p[j-i+1]);
    }
    void init()
    {
        p[0]=1;//p^0为1
        fir(i,1,N-1)
            p[i]=p[i-1]*131;//P进制的位值
    }
    int main()
    {
        init();
        while (++t)
        {
            ans=0;
            scanf("%s",s+1);
            int len=strlen(s+1);
            if (strcmp(s+1,"END")==0) //结束读入
                return 0;
            f2[len+1]=0;//初始化要注意,不然的话容易GG
            fir(i,1,len) 
                f1[i]=f1[i-1]*Mod+(s[i]-'a'+1);//前缀和
            fic(i,len,1)
                f2[i]=f2[i+1]*Mod+(s[i]-'a'+1);//后缀和
            fir(i,1,len)
            {
                l=0,r=min(i-1,len-i);//二分枚举长度为奇数的字符串 记住这里l一定要为0,不然的话,你会发现最后一个数据会卡死你.
                while(l<r)
                {
                    mid=l+r+1>>1;
                    if (Hash1(i-mid,i-1)==Hash2(i+1,i+mid))//如果这是一个回文串的话
                        l=mid;
                    else
                        r=mid-1;
                }
                ans=max(l<<1 | 1,ans);//算出最大长度
                l=0,r=min(i-1,len-i+1);//偶数字符串
                while (l<r)
                {
                    mid=l+r+1>>1;
                    if (Hash1(i-mid,i-1)==Hash2(i,i+mid-1))//check判断
                        l=mid;
                    else
                        r=mid-1;
                }
                ans=max(l<<1,ans);//偶数字符串只需要*2
            }
            printf("Case %d: %d\n",t,ans);
        }
        return 0;
    }

    马拉车算法:代码有详细注释

    #include<bit/stdc++.h>
    #define Min(a,b) a>b?b:a  
    #define Max(a,b) a>b?a:b  
    using namespace std;  
    int Len[3000005];  //记录每个位置的最长回文子串的长度
    char str[3000005],s[3000005];  
    int n,mx,id,len;  //mx记录前面最长回文串的最右位置,id记录前面最长回文子串的下标
    
    void init(){  //初始化函数,将字符串中插入特殊字符
        int k=0;  
        str[k++] = '$';  
        for(int i=0;i<len;i++){  
            str[k++]='#';  
            str[k++]=s[i];  
        }  
        str[k++]='#';  
        len=k;  
    }  
    
    int Manacher(){  
      Len[0] = 0;  
      int sum = 0;  
      mx = 0;  
      for(int i=1;i<len;i++){  
        if(i < mx) Len[i] = Min(mx - i, Len[2 * id - i]);  //如果当前位置比最右位置小,更新当前位置的长度
        else Len[i] = 1;  //比最右位置大,则只能一位一位枚举计算最大回文子串
        while(str[i - Len[i]]== str[i + Len[i]]) Len[i]++;  
        if(Len[i] + i > mx){//如果当前位置的回文子串的最右位置大于前面的,就更新其值  
          mx = Len[i] + i;  
          id = i;      
          sum = Max(sum, Len[i]);  //用sum来记录最大的回文子串长度
        }  
      }  
      return (sum - 1);  
    }  
    
    int main()  
    {  
      scanf("%d",&n);  
      while(n--){  
        memset(str,0,sizeof(str));
        scanf("%s",s);  
        len = strlen(s);  
        init();  
        int temp = Manacher();  
        printf("%d\n",temp);  
      }  
      return 0;  
    }  

     

    展开全文
  • 字符串哈希函数

    千次阅读 2019-01-23 11:05:11
    本文将介绍什么是字符串哈希函数,字符串哈希函数常见用法,以及字符串哈希函数的实现原理和常用算法。 2、概念 哈希之所以广泛存在,是因为它能在绝大多数情况下可以在O(1)的时间复杂度中完成元素的查找。它的...

    原文地址:https://blog.csdn.net/mylinchi/article/details/79508112

    1、简介

    本文将介绍什么是字符串哈希函数,字符串哈希函数常见用法,以及字符串哈希函数的实现原理和常用算法。


    2、概念

    哈希之所以广泛存在,是因为它能在绝大多数情况下可以在O(1)的时间复杂度中完成元素的查找。它的核心是数组,如果输入是一个自然数,那么当然可以在常数时间内搜索到自然数所对应的数组元素了。但在工程实践中,要查找的关键字往往都不是自然数,即使是自然数也有可能是很大的值。因此,只要我们提前把关键字转换为在固定较小范围内的自然数,就可以实现常数时间的查找。那么问题来了,如何实现该转换关系呢?这就是哈希函数所要完成的工作。

    哈希函数:又称散列函数,是把一段有限二进制串(字符串,整数等)转换为自然数的一种函数。
    哈希值:哈希函数输出的最终结果。
    字符串哈希函数:输入是字符串的哈希函数。


    既然是函数,就有可能出现多对一的情况(多个输入对应同一个哈希值),这种情况称为冲突。没有冲突的哈希函数称为完全哈希函数,但这种函数只在理论分析中出现。为了保证每一个元素对应不同的存储地址,可使用以下两类方法:
    链接法:数组元素存储指向链表的指针,链表的每个元素都可以存储一个输入的元素。
    开放地址法:所有的元素都存放在散列表中,它不用指针,是计算出要存取的槽序列。

    使用哈希函数得到哈希值只是Hash算法的第一步,此外一般还需要进行高位运算和取模运算。


    3、常见方法

    评价hash函数性能的一个重要指标就是冲突,在相关资源允许的条件下冲突越少hash函数的性能越好。显然,对于字符串哈希函数,尽可能使每个字符都影响哈希值可以减少冲突。目前常见的字符串hash算法有BKDRHash,APHash,DJBHash,

    JSHash,RSHash,SDBMHash,PJWHash,ELFHash等等。

    使用网上提供的一份英语单词文件:http://www.cs.duke.edu/~ola/ap/linuxwords,共45402个单词,分别比较上面每一个算法在哈希表长度为100,1000和10000时的最大冲突数,理论上平均为455,46和5。结果如下:

    算法长度100的哈希长度1000的哈希长度10000的哈希
    bkdrhash5097214
    aphash5197215
    jshash4946615
    rshash5057415
    sdbmhash5186715
    pjwhash75613134
    elfhash80115891
    djbhash5126417
    dekhash5367522
    bphash1391696690
    fnvhash5166514
    javahash5236916

    其中数据1为100000个字母和数字组成的随机串哈希冲突个数。数据2为100000个有意义的英文句子哈希冲突个数。数据3为数据1的哈希值与 1000003(大素数)求模后存储到线性表中冲突的个数。数据4为数据1的哈希值与10000019(更大素数)求模后存储到线性表中冲突的个数。

    经过比较,得出以上平均得分。平均数为平方平均数。可以发现,BKDRHash无论是在实际效果还是编码实现中,效果都是最突出的。APHash也是较为优秀的算法。DJBHash,JSHash,RSHash与SDBMHash各有千秋。PJWHash与ELFHash效果最差,但得分相似,其算法本质是相似的。


    4、实现原理和常用算法

    所有的字符串哈希算法都是基于对字符编码的迭代运算,只是运算规则不同而已。
    1)BKDRHash算法

    BKDRHash是Kernighan和Dennis在《The C programming language》中提出的。这个算法的常数131是如何选取的,尚未可知,有知情者可以留言。

        // BKDR Hash Function
        unsigned int BKDRHash(char *str)
        {
            unsigned int seed = 131; // 31 131 1313 13131 131313 etc..
            unsigned int hash = 0;
         
            while (*str)
            {
                hash = hash * seed + (*str++);
            }
         
            return (hash & 0x7FFFFFFF);
        }

    关于BKDRHash算法的较为详细的解析可以参考BKDRHash详解。
    2)APHash算法

    Arash Partow提出了这个算法,声称具有很好地分布性。

        // AP Hash Function
        unsigned int APHash(char *str)
        {
            unsigned int hash = 0;
            int i;
         
            for (i=0; *str; i++)
            {
                if ((i & 1) == 0)
                {
                    hash ^= ((hash << 7) ^ (*str++) ^ (hash >> 3));
                }
                else
                {
                    hash ^= (~((hash << 11) ^ (*str++) ^ (hash >> 5)));
                }
            }
         
            return (hash & 0x7FFFFFFF);
        }

    3)DJBHash算法

    Daniel J. Bernstein在comp.lang.c邮件列表中发表的,是距今为止比较高效的哈希函数之一。

        // DJB Hash Function
        unsigned int DJBHash(char *str)
        {
            unsigned int hash = 5381;
         
            while (*str)
            {
                hash += (hash << 5) + (*str++);
            }
         
            return (hash & 0x7FFFFFFF);
        }

    4)JSHash算法

    Justin Sobel提出的基于位的函数函数。

       // JS Hash Function
        unsigned int JSHash(char *str)
        {
            unsigned int hash = 1315423911;
         
            while (*str)
            {
                hash ^= ((hash << 5) + (*str++) + (hash >> 2));
            }
         
            return (hash & 0x7FFFFFFF);
        }

    5)RSHash算法

    其作者是Robert Sedgwicks。实现如下:

        // RS Hash Function
        unsigned int RSHash(char *str)
        {
            unsigned int b = 378551;
            unsigned int a = 63689;
            unsigned int hash = 0;
         
            while (*str)
            {
                hash = hash * a + (*str++);
                a *= b;
            }
         
            return (hash & 0x7FFFFFFF);
        }

    6)SDBMHash算法

    SDBM项目使用的哈希函数,声称对所有的数据集有很好地分布性。

        unsigned int SDBMHash(char *str)
        {
            unsigned int hash = 0;
         
            while (*str)
            {
                // equivalent to: hash = 65599*hash + (*str++);
                hash = (*str++) + (hash << 6) + (hash << 16) - hash;
            }
         
            return (hash & 0x7FFFFFFF);
        }

    7)PJWHash算法

    Peter J. Weinberger在其编译器著作中提出的。

        // P. J. Weinberger Hash Function
        unsigned int PJWHash(char *str)
        {
            unsigned int BitsInUnignedInt = (unsigned int)(sizeof(unsigned int) * 8);
            unsigned int ThreeQuarters    = (unsigned int)((BitsInUnignedInt  * 3) / 4);
            unsigned int OneEighth        = (unsigned int)(BitsInUnignedInt / 8);
            unsigned int HighBits         = (unsigned int)(0xFFFFFFFF) << (BitsInUnignedInt - OneEighth);
            unsigned int hash             = 0;
            unsigned int test             = 0;
         
            while (*str)
            {
                hash = (hash << OneEighth) + (*str++);
                if ((test = hash & HighBits) != 0)
                {
                    hash = ((hash ^ (test >> ThreeQuarters)) & (~HighBits));
                }
            }
         
            return (hash & 0x7FFFFFFF);
        }

    8)ELFHash算法

    Unix系统上面广泛使用的哈希函数。

        // ELF Hash Function
        unsigned int ELFHash(char *str)
        {
            unsigned int hash = 0;
            unsigned int x    = 0;
         
            while (*str)
            {
                hash = (hash << 4) + (*str++);
                if ((x = hash & 0xF0000000L) != 0)
                {
                    hash ^= (x >> 24);
                    hash &= ~x;
                }
            }
         
            return (hash & 0x7FFFFFFF);
        }

    9)DEKHash算法

    Donald E. Knuth在《计算机程序设计的艺术》中提出的哈希函数。

    public static int dekhash(String str) {
          int hash = str.length();
    
          for (int i = 0; i < str.length(); i++) {
             hash = (hash << 5) ^ (hash >> 27) ^ (int)str.charAt(i);
          }
    
          return hash & 0x7FFFFFFF;
       }

    10)BPHash算法

    public static int bphash(String str) {
          int hash = str.length();
    
          for (int i = 0; i < str.length(); i++) {
             hash = (hash << 7) ^ (int)str.charAt(i);
          }
    
          return hash & 0x7FFFFFFF;
       }   

    11)FNVHash算法

    public static int fnvhash(String str) {
          int fnvprime = 0x811C9DC5;
          int hash = 0;
    
          for (int i = 0; i < str.length(); i++) {
             hash *= fnvprime;
             hash ^= (int)str.charAt(i);
          }
    
          return hash & 0x7FFFFFFF;
       }

    12)Java String Hashcode算法

    这是Java的字符串类的Hash算法,简单实用高效。直接从JDK6里面拿出来的代码:

    public static int javahash(String str) {
          int hash = 0;
    
          for (int i = 0; i < str.length(); i++) {
             hash = hash * 31 + (int)str.charAt(i);
          }
    
          return hash & 0x7FFFFFFF;
       }   

    ------------------------------------------------------------------------------------------------------------------------------

    参考文章:https://blog.csdn.net/u012834750/article/details/80005162

    一个股票交易系统的后台,为了能快速查找各种股票代码的Tick,会计算其哈希值,然后存贮在哈希表里面。一个好的哈希函数应该能实现很好地分布性,减少冲突。这里选取了几种常用的字符串哈希,包括BKDRHash,APHash,JSHash,RSHash,

    SDBMHash,PJWHash,ELFHash和DJBHash,通过在不同的字符串集合测试,来测试其性能。

    展开全文
  • 字符串哈希成数字的几种经典的方法:其中的一部分 #ifndef INCLUDE_GENERALHASHFUNCTION_C_H #define INCLUDE_GENERALHASHFUNCTION_C_H #include typedef unsigned int (*hash_function)(char*, unsigned int...
  • 字符串哈希的方法: 在刷一些字符串的题目中是非常实用的,是一个不得不掌握的“套路”。 下面讲述字符串哈希的方法。(主要是利用字符串的前缀哈希) 例如 : 字符串 str = “ABCD”; 第一步: 将上面的字符 映射...

    在这里插入图片描述

    字符串哈希的方法: 在刷一些字符串的题目中是非常实用的,是一个不得不掌握的“套路”。
    下面讲述字符串哈希的方法。(主要是利用字符串的前缀哈希)

    例如 : 字符串 str = “ABCD”;

    第一步: 将上面的字符 映射成从1开始的 A - 1; B - 2 …
    第二步: 将上面的字符串看成是一个P进制的数 (1234)p 。 这里的P 是一个经验值 : 131 或者是13331.
    第三步: 然后将上面的P进制的数转换成 十进制的数字。 然后mod 一个数字264. 为了保证不冲突。
    (1 * P3 + 2 * P2 + 3 * P 1 + 4 * P 0) mod (264).
    这里映射成hash值是保证不冲突的,所以在这里不用考虑冲突处理的方式。

    然后查找是否有相同字符串,就对比这一段的hash值是不是相同。按照上面的方式求的是前缀的hash值,但是中间一段的字符串的hash 值应该如何求呢?
    在这里插入图片描述

    题目:

    给定一个长度为n的字符串,再给定m个询问,每个询问包含四个整数l1,r1,l2,r2,请你判断[l1,r1]和[l2,r2]这两个区间所包含的字符串子串是否完全相同。
    字符串中只包含大小写英文字母和数字。
    输入格式
    第一行包含整数n和m,表示字符串长度和询问次数。
    第二行包含一个长度为n的字符串,字符串中只包含大小写英文字母和数字。
    接下来m行,每行包含四个整数l1,r1,l2,r2,表示一次询问所涉及的两个区间。
    注意,字符串的位置从1开始编号。
    输出格式
    对于每个询问输出一个结果,如果两个字符串子串完全相同则输出“Yes”,否则输出“No”。
    每个结果占一行。
    数据范围
    1≤n,m≤105

    输入样例:
    8 3
    aabbaabb
    1 3 5 7
    1 3 6 8
    1 2 1 2

    输出样例:
    Yes
    No
    Yes

    Code

    #include <iostream>
    using namespace std;
    
    typedef unsigned long long ULL;
    const int N = 100010, P = 131;  // 这里的P 是经验值 131 ,或者13331
    
    int n,m;
    char str[N];
    // 这里用unsigned long long 存储就相当于mod 2 ^ 64, 因为超过了会溢出的
    ULL h[N], p[N];   // h[]是存储字符串哈希值的  p[] 是存储p次方的  
    
    ULL get(int l, int r)
    {
        return h[r] - h[l -1] * p[r - l + 1];     // 区间hash 的公式
    }
    
    int main()
    {
        scanf("%d%d%s", &n, &m, str + 1);
        
        p[0] = 1;
        for(int i=1; i<=n; i++)
        {
            p[i] = p[i-1] * P;  // p数组保存 计算的次方数
            h[i] = h[i-1] * P +str[i];   // 计算字符串的前缀,  后面的是0次 所以直接加上str[i]就行了
        }
        
        while(m -- )
        {
            int l1, r1, l2, r2;
            scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
            if(get(l1, r1) == get(l2, r2)) cout << "Yes" << endl;
            else cout << "No" << endl;
        }
        return 0;
    }
    
    展开全文
  • AcWing - 字符串哈希(字符串查询)

    千次阅读 2019-08-23 11:31:13
    给定一个长度为n的字符串,再给定m个询问,每个询问包含四个整数l1,r1,l2,r2,请你判断[l1,r1]和[l2,r2]这两个区间所包含的字符串子串是否完全相同。 字符串中只包含大小写英文字母和数字。 输入格式 第一行包含....
  • 浅谈字符串哈希

    2018-07-25 13:11:37
     哈希算法是通过一个哈希函数H,将一种数据(如字符串)转化为另一种数据(通常转化为整形数值),有些题可用map做,但数据一大就要用到字符串哈希 二、字符串哈希  寻找长度为n的主串S中的匹配串T(长度为m)...
  • 字符串哈希算法

    千次阅读 2020-02-02 12:37:26
    字符串哈希算法,通俗的理解,就是将一个字符串,转化成整数 原来我们进行字符串匹配的时候,就是一个个去匹配,那么时间复杂度是o(n),如果转化成数字,去匹配那么时间复杂度会变成o(1)。 哈希算法的引入 ...
  • 字符串哈希(Hash)

    千次阅读 多人点赞 2019-06-24 21:04:06
    所谓字符串哈希就是构造一个数字使之唯一代表一个字符串。 构造方法: 假如给你一个数字1166,形式上你只知道它只是1和6的组合,但你知道它代表的实际大小1*10^3+1*10^2+6*10^1+6*10^0。 同理,给你一个字符串,...
  • 字符串哈希(详解+模版)

    千次阅读 2018-12-09 19:17:32
    字符串Hash的种类还是有很多种的,不过在ACM中一般只会用到一种名为“BKDR Hash”的字符串Hash算法。它的主要思路是选取恰当的进制,可以把字符串中的字符看成一个大数字中的每一位数字。关于进制的选择实际上非常...
  • 字符串哈希(BKDR_hash)

    2020-06-27 23:33:32
    这里我们就要用到字符串哈希来降到O(1)。 字符串哈希入门 说得通俗一点,字符串哈希实质上就是把每个不同的字符串转成不同的整数。 为什么会有这样的需要呢?很明显,存储一个超长的字符串和存储一个超大但是能存的...
  • 暴雪字符串哈希.txt

    2020-01-20 11:04:29
    dwHashType = 0时计算的哈希值用于确定字符串哈希表中的位置; dwHashType = 1,dwHashType = 2时计算的哈希值用于验证字符串 返回值:字符串哈希值 */ unsigned long HashString(char *lpszString, ...
  • golang实现字符串哈希 算法题目 给定一个长度为n的字符串,再给定m个询问,每个询问包含四个整数l1,r1,l2,r2,请你判断[l1,r1]和[l2,r2]这两个区间所包含的字符串子串是否完全相同。 字符串中只包含大小写英文字母...
  • 字符串哈希&子串匹配

    2019-09-24 05:55:25
    字符串哈希 字符串哈希就是将一个字符串映射为一个整数,该整数就可以用于vis标记有没有出现过,就不用遍历所有字符串了。 哈希公式:  hash[i] = hash[ i - 1 ] *p + id( s[i] ) 其中id(x)为x-‘a’+1或者...
  • 【算法学习】字符串哈希(Hash)

    千次阅读 2020-01-15 21:11:25
    什么是字符串Hash 构造字符串Hash 1)自然溢出方法 2)单Hash方法 3)双Hash方法 4)三种不同的构造方法的对比 获取子串的Hash O(1) 1)例子 2)公式 具体的题目例子 1)题目链接 2)题意 3)解题分析 ...
  • hsh是一个完全用Rust编写的简单的字符串哈希CLI,它支持多种哈希函数。 它主要依靠来执行哈希。 支持的哈希函数 (GOST R 34.11-9)(同时带有“测试参数”和CryptoPro ) Grøstl224 Grøstl256 Grøstl384 ...
  • 字符串哈希On求最长回文子串

    千次阅读 2020-09-18 20:07:11
    预处理正哈希和逆哈希,如果正哈希[l,r]==逆哈希[l,r],说明两个字符串相同。 枚举回文中心,二分求回文半径。 可以看出,时间复杂度是O(nlogn)O(nlogn)O(nlogn),代码如下: unsigned long long order1[2
  • 字符串哈希到整数函数,算法

    千次阅读 2019-01-06 09:49:09
    基本概念 所谓完美哈希函数,就是指没有冲突的哈希函数,即对任意的 key1 != key2 有h(key1) != h(key2)。 设定义域为X,值域为Y, n=|X|,m=|Y|,那么肯定有m&...在处理大规模字符串数据时,...
  • 这是一道模板题。 给定一个字符串 A 和一个字符串 B ,求 B在 A 中的出现次数。 A和 B中的字符均为英语大写字母或小写字母。 A中不同位置出现的 B可重叠。...字符串哈希模板提 #include<stdio.h> #inclu
  • 哈希HASH的本质思想类似于映射、离散化。 ...本篇总结字符串哈希以及一些应用例题。 为什要用字符串哈希? 因为取出一个字符串是$O(n)$的,比较一遍又是$O(n)$的,况且要比较两个甚至多个。这...
  • package main import "fmt" func main() { hashValue := BKDRHash("Hello,World!") fmt.Print(hashValue) } func BKDRHash(str string) uint64 { seed := uint64(131) // 31 131 1313 13131 131313 etc.. ...
  • 在上一篇文章哈希表的大小提到过一种除留余数法的计算哈希值的函数。这一篇文章来具体说一说,怎么设计哈希函数能够让哈希表更加效率。 哈希函数,是用来计算存储数据的哈希值的,根据存储数据的类型,可以设计不同...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 213,937
精华内容 85,574
关键字:

字符串哈希