【洛谷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;
}
posted @ 2020-11-27 12:00  stoorz  阅读(192)  评论(0编辑  收藏  举报