字符串哈希
字符串哈希
说得通俗一点,字符串哈希实质上就是把每个不同的字符串转成不同的整数。
为什么会有这样的需要呢?很明显,存储一个超长的字符串和存储一个超大但是能存的下的整数,后者所占的空间会少的多,但主要还是为了方便判断一个字符串是否出现过,这是最基础的部分。并保证字符串不同,得到的哈希值不同,这样就可以用来判断一个该字串是否重复出现过。
当然也很容易想到,如果有不同的字符串转成同一个整数,那么区分功能就基本废掉 ,所以我们需要一个算法把每个字符串转成唯一的整数。所以字符串哈希算法就应运而生,哈希算法的难点也就在于如何构造一个合适的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的多少次方。