字符串问题非常好用的一种方法:字符串哈希。
离散化本质上算是一类特殊的哈希算法。
所以哈希算法本质上是把变量通过某种映射关系,从原本的范围对应到新的某个范围。
字符串哈希的常用公式就是,假定字符串str和变量P 和变量Q;
字符串”abcdef“经过哈希的原理,我们将abcdef视作一个p进制的数,并且根据进制转换的原理,按权展开,然后将其转换为十进制的值并且modQ,得到的数就是对应的哈希值。
计算公式:hash[ "abcdef" ] = hash("abcdef") = (a * (p^4) + b * (p^3) + c * (p^2) + e * (p^1) + f * (p^0) ) mod Q;
然后当我们求这个字符串的L - R区间的子串hash值时,我们已知1-L的哈希值和1-R的哈希值,我们只需要把h[L - 1]*p[R-L+1]也就是把l向左移动L-R间距的距离使他们对齐,然后相减就可以得到。也就是说求区间L - R的哈希值公式 = h[R] - h[L - 1] * p [R - L +1];
例题:
#include<bits/stdc++.h> using namespace std; typedef unsigned long long ULL; //经验:我们通常P取131 或者 13331 同时Q取2^64次方,能使得hash的冲突概率最低,达到最小 const ULL P = 131; const int N = 100010; /* 由于2的64次方正好是unsigned long long的最大值的所以溢出之后就等于取余Q const unsigned long long Q = 2的64次方 */ //h[]是存hash值的数组,p[]是存放p的次方的结果的数组 //哈希公式:hash("abcdef") = (a * (p^4) + b * (p^3) + c * (p^2) + e * (p^1) + f * (p^0) ) mod Q; ULL h[N],p[N]; //求区间L-R之间的字符串的hash值 //根据原理,如果想求L 至 R区间的字符串的hash值,我们已知1-L的哈希值和1-R的哈希值,我们只需要把 //h[L]*p[R-L+1]也就是把l向左移动L-R间距的距离使他们对齐,然后相减就可以得到。 ULL get(int l,int r){ ULL res = 0; res = h[r] - h[l-1]*p[r-l+1]; return res; } int n,m; char str[N]; int main(){ ios::sync_with_stdio(0); cin.tie(); cin>>n>>m; //str+1表示str的首地址向右偏移一位,也就是下标从1开始读入 cin>>str+1; //初始化P[0] = 1; p[0] = 1; //预处理p[]数组和h[]数组 for(int i = 1 ; i <= n ; i ++){ p[i] = p[i-1] * P; //h[i-1]*p 意义是把1-(i-1)位的哈希值集体向左移动一位,然后加上当前第i位上的字符*p^0也就是*1 h[i] = h[i-1] * P + str[i]; } while(m--){ //开始进行询问 int l,r,L,R; cin>>l>>r>>L>>R; if(get(l,r) == get(L,R)){ puts("Yes"); }else puts("No"); } return 0; }