文章目录
R e s u l t Result Result
H y p e r l i n k Hyperlink Hyperlink
https://www.luogu.com.cn/problem/U142342?contestId=37784
D e s c r i p t i o n Description Description
给定一个长度为 n n n的字符串, m m m个询问,每次询问它的两个子串是否可以通过字母不重复一一替换得到
数据范围: n , m ≤ 2 × 1 0 5 n,m\leq 2\times 10^5 n,m≤2×105
S o l u t i o n Solution Solution
显然只要字母的相对排列相同就好了,这一点我们可以
h
a
s
h
hash
hash处理出来
关键在于如何搞定恶心人的替换,既然是替换,自然我们尽量让
h
a
s
h
hash
hash值相同对匹配上就好了,由于字符集很小只有26个,所以我们排序再比较的复杂度是
K
=
26
log
26
K=26\log 26
K=26log26的,总得复杂度
O
(
n
+
m
K
)
O(n+mK)
O(n+mK)
提供另一种聪爷的做法【比赛的时候也有想到过,如图】
最后两句话是错的,应该是减去前面的值在对应的
h
a
s
h
hash
hash比较一下即可,还是因为字符集只有26个,这种比较方法的常数是
K
=
26
K=26
K=26的,更小一点
代码采用的是前者
C o d e Code Code
#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define ULL unsigned long long
using namespace std;int n,m,x,y,z,num;
char str[200010];
ULL pw[200010],s[200010][26],a[26],b[26];
inline LL read()
{
char c;LL d=1,f=0;
while(c=getchar(),!isdigit(c)) if(c=='-') d=-1;f=(f<<3)+(f<<1)+c-48;
while(c=getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48;
return d*f;
}
inline bool ck()
{
for(register int i=0;i<26;i++) if(a[i]!=b[i]) return false;
return true;
}
signed main()
{
n=read();m=read();
scanf("%s",str+1);pw[0]=1;
for(register int i=1;i<=n;i++)
{
pw[i]=pw[i-1]*233;
for(register int j=0;j<26;j++) s[i][j]=s[i-1][j]*233;s[i][str[i]-97]++;
}
while(m--)
{
x=read();y=read();z=read();
for(register int i=0;i<26;i++)
a[i]=s[x+z-1][i]-s[x-1][i]*pw[z],b[i]=s[y+z-1][i]-s[y-1][i]*pw[z];
sort(a,a+26);sort(b,b+26);
puts(ck()?"YES":"NO");
}
}