AcWing 841. 字串雜湊
阿新 • • 發佈:2022-02-22
給定一個長度為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
思路:
字串雜湊,目的是將一個字串字首轉換一個\(mod 2^{64}\)的整數,將一個字串轉化成P進位制,其中P經常取得是131或者13331,然後取模使用自然取模法,就是使用 unsigned long long
來儲存字首和,自然溢位後就是取模得結果。
將字串的字首轉換為數來存值
由於每位的權值是不一樣的 所以每個字首值都對應著唯一的一種字串
所以相減後的值也應該是唯一的 從而利用相減後的值可以判斷字串的區間段是否相等
#include<iostream> #include<cstdio> #include<cstring> using namespace std; // 小技巧:取模的數用2^{64},這樣直接用unsigned long long儲存,溢位的結果就是取模的結果 typedef unsigned long long ULL; const int N = 100010; char str[N]; ULL h[N], p[N]; // h表示一個字串的字首和 // p表示各個位的權重 int P = 131; // 131 或者 13331 ULL get(int l, int r) { return h[r] - h[l-1]*p[r-l+1]; } int main() { int n, m; scanf("%d%d", &n, &m); scanf("%s",str+1);// 因為前面可能空格? for(int i = 1; i < n; i++ ) { p[0] = 1; for(int i = 1; i <= n; i++) { p[i] = p[i-1] * P; // h[i]表示各位的權重 h[i] = h[i-1]*P + str[i]; // h[i]表示各位的字首和 } } 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; }
參考
https://blog.csdn.net/weixin_43910320/article/details/107718725
https://blog.csdn.net/wangqianqianya/article/details/89670919