1. 程式人生 > 其它 >AcWing 841. 字串雜湊

AcWing 841. 字串雜湊

給定一個長度為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