字符串問題非常好用的一種方法:字符串哈希。
離散化本質上算是一類特殊的哈希算法。
所以哈希算法本質上是把變量通過某種映射關系,從原本的范圍對應到新的某個范圍。
字符串哈希的常用公式就是,假定字符串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; }