思路:
这个题我在蓝桥训练的时候确实做过类似的了,但那个是特定长度,对于这个题每次k都在变我就不太会处理了,而且我也考虑过相乘会爆 l l 的想法,其实不要紧的,orz;
这种题目的话匹配肯定是要构造一个哈希函数的,主要看是怎么构造,官方题解说是给每个数字一个随机的64位整数,然后用前缀和当一个集合的哈希值就可以了,我当时很纳闷,如果这样的话那2 2 2 2 和1 1 3 3 不也匹配了吧,其实毕竟我是第一次接触哈希这种东西,对他理解的太浅薄,我们构造的哈希函数只是减少了冲突,使冲突的几率越小越好而不是一定就不冲突的,对于这个题我们随机分配一个64位整数然后在乘起来这个哈希值是足够的.
这里我构造的哈希函数的话是用了五个大素数zi,然后对于给定的每个s,我都用它去 sigma(s*(zi,s));作为s的哈希值,再加上ll的溢出就冲突很少了,然后记录一个前缀和,对于每次验证的密钥我们也这样求一个哈希值,最后枚举原串s中没k个长度的和去匹配就好了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
const ll num[10]={22717,31873,37987,40087,49663};
ll s[maxn];
ll t[maxn];
int a[maxn],b;
int n,m,k;
ll sum1[maxn];
int vis1[maxn],vis2[maxn];
ll quick(ll a,ll b)
{
ll res=1;
while(b)
{
if(b&1)
res*=a;
a*=a;
b>>=1;
}
return res;
}
ll f(ll x)
{
ll ans=0;
for(int i=0;i<5;i++)
{
ans+=x*quick(num[i],x);
}
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(vis1[a[i]]==0)
{
vis1[a[i]]=1;
s[a[i]]=f(a[i]);
}
// printf("%lld\n",s[a[i]]);
sum1[i]=sum1[i-1]+s[a[i]]+a[i];
}
scanf("%d",&m);
while(m--)
{
int flag=0;
memset(vis2,0,sizeof(vis2));
ll sum2=0;
scanf("%d",&k);
for(int i=1;i<=k;i++)
{
scanf("%d",&b);
if(vis2[b]==0)
{
if(vis1[b]==0)
flag=1;
t[b]=f(b);
vis2[b]=1;
}
sum2+=t[b]+b;
}
//printf("%lld\n",sum2);
if(flag)
printf("0\n");
else
{
ll cnt=0;
for(int i=k;i<=n;i++)
{
if(sum1[i]-sum1[i-k]==sum2)
cnt++;
}
printf("%lld\n",cnt);
}
}
return 0;
}