专栏文章

Hash Killer 两题题解

个人记录参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mip6nuay
此快照首次捕获于
2025/12/03 07:02
3 个月前
此快照最后确认于
2025/12/03 07:02
3 个月前
查看原文

Hash killer 1

Solve:

神犇思维,构造奇异数据
C
class Solution_Case
{
	/*
	题解:
	1.https://blog.csdn.net/weixin_45750972/article/details/107457997
	2.oiwiki
	3.https://blog.csdn.net/m0_46209312/article/details/105635114
	结合着看。。。

	首先明白两点:
	1.卡hash的关键在于构造两个不同的串对应的hash值相同。
	2.爆u64相当于对2^64这个数取模。
	如果base是偶数,那么a.........aaa(>64个a)与ba.......aa(a的数量为前面那么串a的数量-1),这两个串长度相同,hash值相同,显然串是不同的,这样就卡掉了。
	如果base是奇数,就比较麻烦了。

	看vfk的做法吧:
	a \ b表示a能整除b。(orz 具体数学)
	strA . strB代表字符串串联。如"娃" . "哈哈" = "娃哈哈"
	设字符串序列{orzstr[i]},orzstr[1] = "a", orzstr[i] = orzstr[i - 1] . not(orzstr[i - 1])
	设hash(str)为str的哈希值。

	hash(orzstr[i]) = hash(orzstr[i - 1]) * base ^ |not(orzstr[i - 1])| + hash(not(orzstr[i - 1]))
		= hash(orzstr[i - 1]) * base ^ (2 ^ (i - 2)) + hash(not(orzstr[i - 1]))
		hash(not(orzstr[i - 1])) * base ^ (2 ^ (i - 2)) + hash(orzstr[i - 1])
		hash(orzstr[i]) - hash(not(orzstr[i]))
	= (hash(orzstr[i - 1]) - hash(not(orzstr[i - 1]))) * (base ^ (2 ^ (i - 2)) - 1)

	hash(not(orzstr[i]))似乎是个神奇的东西。
	而我们的目的实际上是要找两个字符串strA, strB使得:hash(strA) % 2^64 = hash(strB) % 2^64
	相当与 2^64 \ hash(strA) - hash(strB)

	设数列{f[i]},f[i] = hash(orzstr[i]) - hash(not(orzstr[i]))
	f[i] = f[i - 1] * (base ^ (2 ^ (i - 2)) - 1)
	base ^ (2 ^ (i - 1)) - 1
	f[i] = f[i - 1] * g[i - 1]

	问题是不是结束了呢……
	发现没有……
	这样的话我们要使2 ^ 64 \ f[i],至少得让i = 65……然后发现|orzstr[65]|是个天文数字。

	i > 1时有:
	base ^ (2 ^ (i - 1)) - 1 = (base ^ (2 ^ (i - 2)) - 1) * (base ^ (2 ^ (i - 2)) + 1) = g[i - 1] * 一个偶数
	那么4 \ g[2],8 \ g[3]...

	所以f[i] 实际上有:
	2 ^ (i * (i - 1) / 2) \ f[i]

	这就是卡base为奇数时的方法。orzstr[12]和not(orzstr[12])即为所求。
	而读入中base既有奇数又有偶数,直接在奇数构造的字符串后面加64个a就可以了。
	*/
};

#include<bits/stdc++.h>
#define N (1<<13)+5
using namespace std;

char s[N];
signed main()
{
	int n = 1;
	s[0] = 'a';
	for (int i = 0; i < 12; i++, n <<= 1)
		for (int j = 0; j < n; j++)
			s[j + n] = s[j] == 'a' ? 'b' : 'a';
	printf("%d %d\n", n + 64, n >> 1);
	printf("%s", s);
	for (int i = 0; i < 64; i++) putchar('a');
	return 0;
}

Hash Killer 2

Solve:

神笔证明
随机数卡概率可以过
证明部分log出来的要向上取整
话说取整(7)以后算出来的概率是0.45多,如果不取整的话(6.36)概率为0.993,计算是按不取整算?
C
class Solution_Class
{
	/*
	https://oi-wiki.org/string/hash/#hash-%E5%86%B2%E7%AA%81
	神秘的概率以及神密的泰勒展开,神秘的e
	神密的化简,计算出神秘的概率公式

	exp代表以e为底的指数函数

	*/
};

#include<bits/stdc++.h>
#define int long long
#define mod 1000000007
using namespace std;

class Prove
{
  public:
	//我们设 Hash 的取值空间(所有可能出现的字符串的数量)为d
	//计算次数(要计算的字符串数量)为n。
	double Accident_P(int n, int d)
	{
		double A = n*(n - 1), B = -2 * d;
		return 1 - exp(A / B);
	}
	double Log(int A, int x)	//求log  //神笔换底公式
	{
		return log(x) / log(A);
	}
	signed main()
	{
		cout << Log(26, mod) << "\n"; //7
		cout << Accident_P(100000, pow(26, 7)) << "\n";
		return 0;
	}
} sb;

mt19937 Rand(time(NULL)); //默认int类型随机数

int RandNum(int Min, int Max)
{
	int x = Min + Rand() % (Max - Min + 1);
	return x;
}

char getC()
{
	return RandNum('a', 'z');
}

signed main()
{
	//sb.main();
	cout << "100000 7\n";
	for (int i = 1; i <= 100000; i++)
	{
		putchar(getC());
	}
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...