1. 程式人生 > 實用技巧 >【洛谷U142342】詢問

【洛谷U142342】詢問

題目

題目連結:https://www.luogu.com.cn/problem/U142342?contestId=37784
如果兩個字串 a 和 b,如果可以通過將 a 中的 \(26\) 種字母一一對應的替換為不重複的 \(26\) 種字母變成 b 的話,我們就稱 a 和 b 是等價的。
例如 "zzpzpt" 和 "oofofc" 是等價的,"rrrtt" 和 "ooopp" 也是等價的,而 "qqq" 和 "ppq" 就不是,"apple" 和 "abcde" 也不是。
有一個均由小寫字母構成字串 s,長度為 \(n\)
\(m\) 個詢問,每個詢問給定三個數字 \(x,y,z\),詢問 s 中以 \(x\)

位置開頭的長度為 \(z\) 的子串和以 \(y\) 位置開頭的長度為 \(z\) 的子串是否等價。

思路

隨便取一個字母 \(a\),在字串中如果第 \(i\) 為是字母 \(a\) 我們就設為 \(1\),否則設為 \(0\)
那麼兩個子串等價其實就是存在一種兩兩對應的方案,且在對應的區間內他們的 \(01\) 串相同。
那麼將每個字母的 \(01\) 序列 hash 一下,然後就可以 \(O(26)\) 判斷了。
時間複雜度 \(O(26n)\)

程式碼

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;

const int N=200010;
const ull base1=131,base2=13331;
ull ha[3][27][N],power[3][N],h[5][27];
int n,m;
char ch;

int main()
{
	scanf("%d%d",&n,&m);
	power[1][0]=power[2][0]=1;
	for (int i=1;i<=n;i++)
	{
		while (ch=getchar())
			if (ch>='a' && ch<='z') break;
		for (int j=1;j<=26;j++)
		{
			ha[1][j][i]=ha[1][j][i-1]*base1+(j==ch-'a'+1);
			ha[2][j][i]=ha[2][j][i-1]*base2+(j==ch-'a'+1);
		}
		power[1][i]=power[1][i-1]*base1;
		power[2][i]=power[2][i-1]*base2;
	}
	while (m--)
	{
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z); 
		for (int i=1;i<=26;i++)
		{
			h[1][i]=ha[1][i][x+z-1]-ha[1][i][x-1]*power[1][z];
			h[2][i]=ha[1][i][y+z-1]-ha[1][i][y-1]*power[1][z];
			h[3][i]=ha[2][i][x+z-1]-ha[2][i][x-1]*power[2][z];
			h[4][i]=ha[2][i][y+z-1]-ha[2][i][y-1]*power[2][z];
		}
		sort(h[1]+1,h[1]+27); sort(h[2]+1,h[2]+27);
		sort(h[3]+1,h[3]+27); sort(h[4]+1,h[4]+27);
		bool flag=1;
		for (int i=1;i<=26;i++)
			if (h[1][i]!=h[2][i] || h[3][i]!=h[4][i]) flag=0;
		if (flag==1) printf("YES\n");
			else printf("NO\n");
	}
	return 0;
}