计蒜客 UCloud 的安全秘钥(中等) 构造hash

题目链接

思路:

 这个题我在蓝桥训练的时候确实做过类似的了,但那个是特定长度,对于这个题每次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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Marcus-Bao

万水千山总是情,只给五角行不行

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值