字符串哈希

字符串哈希

  说得通俗一点,字符串哈希实质上就是把每个不同的字符串转成不同的整数

  为什么会有这样的需要呢?很明显,存储一个超长的字符串和存储一个超大但是能存的下的整数,后者所占的空间会少的多,但主要还是为了方便判断一个字符串是否出现过,这是最基础的部分。并保证字符串不同,得到的哈希值不同,这样就可以用来判断一个该字串是否重复出现过。

  当然也很容易想到,如果有不同的字符串转成同一个整数,那么区分功能就基本废掉 ,所以我们需要一个算法把每个字符串转成唯一的整数。所以字符串哈希算法就应运而生,哈希算法的难点也就在于如何构造一个合适的Hash函数来满足我们的需求。

  而这里我们采取的办法就是将每一个字符串看成是一个P进制的数字,那么我们将其转化为十进制之后模上一个数M之后再映射到相应的数组中,这就是字符串hash(其中一种办法)

  我们的经验做法是将P取成131或者13331,将M取为2^64(这时99。99%的情况下是认为不会产生冲突)。此时我们只需对数组开一个unsigned long long,因为这时超过2^64系统就会自动取模。

  这时我们需要求某一区间的字符串时就是用h[r]-h[l-1]*P^(r-l+1)。

代码:

#include<iostream>
using namespace std;
typedef unsigned long long ull; 
const int N=1000010,P=131;
int n,m;
char str[N];
ull p[N],h[N];//p数组用来存储P的多少次方,h用来映射字符串前缀的哈希值 
int get(int l,int r)
{
    return h[r]-h[l-1]*p[r-l+1];//公式计算字符串里面某一子段的哈希值 
}
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];//前缀的哈希值变化
        //前缀字符串的个数增加一就是前面的值乘以P以后加上当前的字符值 
    }
    while(m--)
    {
        int l1,r1,l2,r2;
        scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
        if(get(l1,r1)==get(l2,r2)) puts("Yes");
        else puts("No");
    }
    return 0;
}

由于我们需要求得P^(l-r+1),所以我们开一个p数组来储存p的多少次方。

posted @ 2020-11-06 19:47  筱翼深凉  阅读(223)  评论(0编辑  收藏  举报