只要你跑的够快,锅就追不上你

0%

「EOJ 181H」Hash(随机化)

题目大意

给定一个长度不超过 $14$ 的字符串,以及素数 $m$ 和正整数 $p (p > 1)$。要求构造一个长度不超过 $14$ 的字符串,它不等于原字符串,并且以 $p$ 为进制,$m$ 为模数的哈希值等于原串的哈希值。

字符集大小为 $63$,$m \le 10^{12}, p \le 2000, p^{13} > m$。

思路分析

考虑 BSGS 算法。

我们先随机 $k$ 个长度为 $7$ 的串,将哈希值存进 std::unordered_map 中。再随机多个长度为 $7$ 的串,每次将这个串放在答案串的前半段中,再在 map 中查询后半段是否有串可以拼上去,满足整个串的哈希值为目标哈希值。找到后输出答案并退出即可。

我们将一个串的哈希值看作是 $[0, m)$ 中的随机数。不难发现期望只需要随机 $\frac{m}{k}$ 次。故 $k$ 取 $\sqrt{m}$ 最优,时间复杂度 $O(\sqrt m)$。

代码实现

1
#include <cstdio>
2
#include <cstdlib>
3
#include <cstring>
4
#include <unordered_map>
5
using namespace std;
6
7
typedef long long ll;
8
const int maxn = 14, maxm = 1e6;
9
int n, p, k;
10
char s[maxn + 3], ch[100], t[10];
11
ll m, goal;
12
unordered_map<ll, int> mp;
13
14
struct string7 {
15
	char s[7];
16
	ll h;
17
} s7[maxm + 3];
18
19
void prework() {
20
	srand(19260817);
21
	for (int i = 0; i < 26; i++) {
22
		ch[k++] = 'a' + i;
23
	}
24
	for (int i = 0; i < 26; i++) {
25
		ch[k++] = 'A' + i;
26
	}
27
	for (int i = 0; i < 10; i++) {
28
		ch[k++] = '0' + i;
29
	}
30
	ch[k++] = '_';
31
	for (int i = 0; i < maxm; i++) {
32
		ll &x = s7[i].h;
33
		for (int j = 0; j < 7; j++) {
34
			s7[i].s[j] = ch[rand() % k];
35
			x = (x * p + s7[i].s[j]) % m;
36
		}
37
		mp[x] = i;
38
	}
39
}
40
41
int main() {
42
	scanf("%s %lld %d", s, &m, &p);
43
	n = strlen(s);
44
	for (int i = 0; i < n; i++) {
45
		goal = (goal * p + s[i]) % m;
46
	}
47
	prework();
48
	ll p7 = 1;
49
	for (int i = 0; i < 7; i++) {
50
		p7 = p7 * p % m;
51
	}
52
	while (true) {
53
		ll x = 0;
54
		for (int i = 0; i < 7; i++) {
55
			t[i] = ch[rand() % k];
56
			x = (x * p + t[i]) % m;
57
		}
58
		x = (__int128) x * p7 % m;
59
		ll d = goal - x;
60
		d < 0 ? d += m : 0;
61
		if (mp.count(d)) {
62
			int id = mp[d];
63
			printf("%s", t);
64
			puts(s7[id].s);
65
			break;
66
		}
67
	}
68
	return 0;
69
}