计蒜客 UCloud 的安全秘钥(随机化+Hash)

题目链接 UCloud 的安全秘钥

对于简单的版本,我们直接枚举每个子序列,然后sort一下判断是否完全一样即可。

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b)	for (int i(a); i >= (b); --i)

const int N = 200010;

int n;
int a[N], b[N], c[N];
int m,q;

int main(){

	scanf("%d", &n);
	rep(i, 1, n) scanf("%d", a + i);

	scanf("%d", &q);

	while (q--){
		scanf("%d", &m);
		rep(i, 1, m) scanf("%d", b + i);

		if (m > n){
			puts("0");
			continue;
		}

		sort(b +1, b  + m + 1);

		int ans = 0;

		rep(i, 1, n - m +1){
			rep(j, 1, m) c[j] = a[i + j - 1];
			sort(c + 1, c + m + 1);
			bool fl = true;
			rep(j, 1, m) if (c[j] != b[j]){
				fl = false;
				break;
			}

			if (fl) ++ans;
		}

		printf("%d\n", ans);
	}

	return 0;

}

对于中等版本,这个时候不能在判断两个序列是否相似上面花太多的条件。

这个时候就想到了Hash

对$1$到$n$的每一个数,随机一个权值。

两个序列相似则有这两个序列的每个元素的Hash和相等

那么就可以维护一个Hash值的前缀和,判断的时候$O(1)$完成。

但Hash和相等只是必要条件,并不是充分条件,当数据大的时候很容易出错。

所以我随机了四组Hash值,仅当四组Hash值都与原串相等时才算相似。

这样出错概率就基本为零了

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b)	for (int i(a); i >= (b); --i)

const int N = 50010;
const long long mod = 1000007;

int n;
long long w1[N], w2[N], w3[N], w4[N];
long long c1[N], c2[N], c3[N], c4[N];
int a[N];
int q, m;
int b[N << 2];

int main(){

	srand(0);
	scanf("%d", &n);

	rep(i, 1, n) w1[i] = rand() % mod;
	rep(i, 1, n) w2[i] = rand() % mod;
	rep(i, 1, n) w3[i] = rand() % mod;
	rep(i, 1, n) w4[i] = rand() % mod;

	rep(i, 1, n) scanf("%d", a + i);

	scanf("%d", &q);
	while (q--){

		int ans = 0;
		scanf("%d", &m);
		rep(i, 1, m) scanf("%d", b + i);
		long long st1 = 0, st2 = 0, st3 = 0, st4 = 0;
		rep(i, 1, m){
			st1 += w1[b[i]];
			st2 += w2[b[i]];
			st3 += w3[b[i]];
			st4 += w4[b[i]];
		}

		memset(c1, 0, sizeof c1);
		memset(c2, 0, sizeof c2);
		memset(c3, 0, sizeof c3);
		memset(c4, 0, sizeof c4);

		rep(i, 1, n){
			c1[i] = c1[i - 1] + w1[a[i]];
			c2[i] = c2[i - 1] + w2[a[i]];
			c3[i] = c3[i - 1] + w3[a[i]];
			c4[i] = c4[i - 1] + w4[a[i]];
		}

		rep(i, 1, n - m + 1){
			long long n1 = c1[i + m - 1] - c1[i - 1];
			long long n2 = c2[i + m - 1] - c2[i - 1];
			long long n3 = c3[i + m - 1] - c3[i - 1];
			long long n4 = c4[i + m - 1] - c4[i - 1];
			if (n1 == st1 && n2 == st2 && n3 == st3 && n4 == st4) ++ans;
		}
		printf("%d\n", ans);
	}

	return 0;

}

对于困难版本,如果m比较小,则枚举连续子序列的时间会很长。

那么考虑把m小的时候的答案全部塞到map里,询问的时候直接拿出来。

不过Hash的时候还是要保证至少两组,不然出错的概率相当大。

我也是调了好久才卡过去的QAQ

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b)	for (int i(a); i >= (b); --i)

typedef long long LL;

const int L  = 2;
const int N  = 50010;
const LL mod = 1000000007;

int S = 10;
int n, m, q;
int a[N];
LL Hash[N][L << 1];
LL c[N][L << 1], s[N][L << 1];
LL hash_now[L << 1];
map <LL, int> mp[20][L << 1];

int main(){

	scanf("%d", &n);
	rep(i, 1, n) scanf("%d", a + i);

	srand(time(0));
	rep(op, 0, L - 1){
		rep(i, 1, n){
		       Hash[i][op] = (LL)rand();
		}
	}

	rep(i, 1, n){
		rep(j, 0, L - 1){
			c[i][j] = Hash[a[i]][j];
			s[i][j] = s[i - 1][j] + c[i][j];

		}
	}

	S = min(S, n);
	rep(len, 1, S){
		rep(i, 1, n - len + 1){
			rep(j, 0, L - 1){
				++mp[len][j][s[i + len - 1][j] - s[i - 1][j]];

			}
		}
	}

	for (scanf("%d", &q); q--;){
		scanf("%d", &m);
		memset(hash_now, 0, sizeof hash_now);

		rep(i, 1, m){
			int x;
			scanf("%d", &x);
			rep(j, 0, L - 1) hash_now[j] += Hash[x][j];
		}

		if (m <= S){
			int ans = 1 << 30;
			rep(i, 0, L - 1) ans = min(ans, mp[m][i][hash_now[i]]);
			printf("%d\n", ans);
			continue;
		}

		if (m > n){
			puts("0");
			continue;
		}

		int ans = 0;

		rep(i, 1, n - m + 1){
			bool fl = true;
			rep(j, 0, L - 1) if (s[i + m - 1][j] - s[i - 1][j] != hash_now[j]) fl = false;
			if (fl) ++ans;
		}

		printf("%d\n", ans);
	}

	return 0;
}
时间: 2024-10-18 05:56:58

计蒜客 UCloud 的安全秘钥(随机化+Hash)的相关文章

计蒜客 UCloud 的安全秘钥 ——(hash)

题目链接:https://nanti.jisuanke.com/t/15769. 题意是求可以变换位置以后相同的子串有多少个,那么做法是只要每个数字的平方和,立方和以及四次方和都相同就可以了. 代码如下: 1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <map> 5 using namespace std; 6 typedef long long LL;

计蒜客 UCloud 的安全秘钥(困难)(哈希)

UCloud 的安全秘钥(困难) 编辑代码 9.53% 1200ms 262144K 每个 UCloud 用户会构造一个由数字序列组成的秘钥,用于对服务器进行各种操作.作为一家安全可信的云计算平台,秘钥的安全性至关重要.因此,UCloud 每年会对用户的秘钥进行安全性评估,具体的评估方法如下: 首先,定义两个由数字序列组成的秘钥 aa 和 bb 近似匹配(\approx≈) 的关系.aa 和 bb 近似匹配当且仅当同时满足以下两个条件: |a|=|b|∣a∣=∣b∣,即 aa 串和 bb 串长度

计蒜课/UCloud 的安全秘钥(hash)

题目链接:https://nanti.jisuanke.com/t/15768 题意:中文题诶- 思路:直接hash就好了,当时zz了没想到... 代码: 1 #include <iostream> 2 #include <stdio.h> 3 #define ll long long 4 using namespace std; 5 6 const int MAXN = 1e5; 7 ll a[MAXN] = {1}; 8 ll w[MAXN]; 9 10 int main(vo

2017计蒜客(四,五,六)

2017 计蒜之道 初赛 第四场 rank 178 题解 1A 1 #include <bits/stdc++.h> 2 using namespace std; 3 int n,m,k,v[110][110]; 4 void setrow(int c) 5 { 6 for(int j=1;j<=m;j++) v[c][j]=1; 7 } 8 void setcol(int c) 9 { 10 for(int j=1;j<=n;j++) v[j][c]=1; 11 } 12 int

UCloud 的安全秘钥

UCloud 的安全秘钥(困难) 1200ms 262144K 每个 UCloud 用户会构造一个由数字序列组成的秘钥,用于对服务器进行各种操作.作为一家安全可信的云计算平台,秘钥的安全性至关重要.因此,UCloud 每年会对用户的秘钥进行安全性评估,具体的评估方法如下: 首先,定义两个由数字序列组成的秘钥 aa 和 bb 近似匹配(\approx≍) 的关系.aa 和 bb 近似匹配当且仅当同时满足以下两个条件: |a|=|b|∣a∣=∣b∣,即 aa 串和 bb 串长度相等. 对于每种数字 

计蒜客 无脑博士和他的试管们

无脑博士有三个容量分别是A,B,C升的试管,A,B,C分别是三个从1到20的整数,最初,A和B试管都是空的,而C试管是装满硫酸铜溶液的.有时,无脑博士把硫酸铜溶液从一个试管倒到另一个试管中,直到被灌试管装满或原试管空了.当然每一次灌注都是完全的.由于无脑博士天天这么折腾,早已熟练,溶液在倒的过程中不会有丢失. 写一个程序去帮助无脑博士找出当A是个是空的时候,C试管中硫酸铜溶液所剩量的所有可能性. 输入包括一行,为空格分隔开的三个数,分别为整数A,B和C. 输出包括一行,升序地列出当A试管是空的时

简单斐波那契——计蒜客(4)

题目来自“计蒜客”第4题. 解算法题之前,务必先写出与之对应的数学表达式,用于描述算法. 数学描述如图: 根据“数学描述“,写出代码如下: #include <stdio.h> int main() { int N =0 ; scanf("%d", &N); int i, fn1 = 1, fn2 = 0, fn; switch(N) { case 0: printf("0"); break; case 1: printf("1&quo

计蒜客普及组模拟赛

今天没事闲的看到计蒜客有个普及组模拟赛,就当练了练手去打了,成绩低的可怜...400分崩成280分AK梦想化作泡影 第一题 同学的爱好 链接:https://nanti.jisuanke.com/t/17291 小学应用题难度?大概画个图就能懂,把每个部分都标上号,算出a,b,c,d,e,f的部分,进行运算就行了. 不多解释了,直接上代码 #include<iostream> #include<cstdio> #include<algorithm> #include&l

计蒜客 作弊揭发者(string的应用)

鉴于我市拥堵的交通状况,市政交管部门经过听证决定在道路两侧安置自动停车收费系统.当车辆驶入车位,系统会通过配有的摄像头拍摄车辆画面,通过识别车牌上的数字.字母序列识别车牌,通过连接车管所车辆信息数据库确认车辆,进行扣费. 斗智斗勇的好戏一般从此处展开… 一些车主通过在停车时遮挡车牌上的一个或多个数字.字母序列,来阻碍识别系统的识别工作,以此逃避停车费用的缴纳. 车主这简直是用轻轻的一挡搞出来一个世界难题有木有?!管理是一方面,技术解决才是王道啊. 这么难的项目不得不交给计蒜客实验室了.D 神负责