题解:P10474 [BeiJing2011] Matrix 矩阵哈希
前言
事情的起因是,我在用别的平台上课时碰到了这道题不太会,来这道题在洛谷的题解区转了一圈,发现好像大家的代码都过不了那个平台上的题,有一个点总是 TLE 。在请教完老师后,决定来发一篇题解。
本题的主要思路
其实这道题就是一个二维哈希。关于这一点的知识大家可以去看其他的题解,讲的都非常详细。我在这里就只作简单讲解。
//二维 Hash 模板
p1[0] = p2[0] = 1;
for (int i = 1; i <= n; i++) p1[i] = p1[i - 1] * P1;
for (int i = 1; i <= m; i++) p2[i] = p2[i - 1] * P2;
for (int i = 1; i <= n; i++)
{
ULL c = 0;
scanf("%s", s+1);
for (int j = 1; j <= m; j++)
{
c = c * P2 + s[j];
h[i][j] = h[i - 1][j] * P1 + c;
}
}
//先不用管下面的这个 insert ()
for (int i = A; i <= n; i++)
for (int j = B; j <= m; j++)
insert(h[i][j] - h[i - A][j] * p1[A] - h[i][j - B] * p2[B] + h[i - A][j - B] * p1[A] * p2[B]);
这就是一个很普通的二维 Hash 模板了。二维 Hash 和一维 Hash 大体上是一样的,只不过要采用二维前缀和的思想来运算和求值。
时间问题的发现
关于解决时间上的问题,这里使用哈希表会比使用 map 要快,原因也显而易见,map 的每一个操作都有一个无法去除的 log ,这就是导致时间过慢的主要原因。
时间问题的解决
上面也说到了,就是使用哈希表。我这里就讲一下本题中使用到的 insert() , 和 find() 函数,对于它的其他用法可以参考 OI Wiki。
首先,我要先引入哈希表的概念。哈希表的原理与map类似,都是使用一个关键词( key )来查找一个值( value )。使用一个数组来建立 key 与 value 的映射关系即可。
但有些时候 key 并不是一个整数,我们就需要用 Hash 来把它换成整数,可问题出现了。 Hash 值过大时,就没有办法使用数组下标来查找了。因此我们可以取模。
但取模后, key 是很容易重复的(冲突),对于这种情况我们可以这样来办。把同一个 key 的 value 用链表存起来,每次查找值的时候,只需要扫一遍对应 key 的链表即可。
以上所说即为 find() 函数。
// find() 函数
int find(ULL x)
{
int id = x % mod;
for (int i = last[id]; i; i = nxt[i])
if (num[i] == x) return i;
return 0;
}
好,现在我们把目光移到第一段代码上,注意那个 insert() 函数。 insert() 函数,顾名思义是用来插入的。前面已经提到过,哈希表使用链表来存的,所以此处使用像链表一样的插入方式。
// insert() 函数
int insert(ULL x)
{
//这一步是为了查重,与 find() 函数一致
int id = x % mod;
for (int i = last[id]; i; i = nxt[i])
if (num[i] == x) return i;
// num[] 就是存数据的链表, nxt[] 是存指针的, last[] 就是每一个表的头指针。
num[++cnt] = x;
nxt[cnt] = last[id];
last[id] = cnt;
return cnt;
}
如果你到现在全部看懂了的话,那么恭喜你,你已经会了!
最终代码
这里放上无注释版本的代码,方便各位粘贴。
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
const int P1 = 10003, P2 = 11117, mod = 1000007, N = 1005;
int n, m, T, A, B;
char s[N];
ULL p1[N], p2[N], h[N][N], num[N * N];
int cnt, last[mod], nxt[N * N];
int find(ULL x)
{
int id = x % mod;
for (int i = last[id]; i; i = nxt[i])
if (num[i] == x) return i;
return 0;
}
int insert(ULL x)
{
int id = x % mod;
for (int i = last[id]; i; i = nxt[i])
if (num[i] == x) return i;
num[++cnt] = x;
nxt[cnt] = last[id];
last[id] = cnt;
return cnt;
}
int main()
{
cin >> n >> m >> A >> B;
p1[0] = p2[0] = 1;
for (int i = 1; i <= n; i++) p1[i] = p1[i - 1] * P1;
for (int i = 1; i <= m; i++) p2[i] = p2[i - 1] * P2;
for (int i = 1; i <= n; i++)
{
ULL c = 0;
scanf("%s", s+1);
for (int j = 1; j <= m; j++)
{
c = c * P2 + s[j];
h[i][j] = h[i - 1][j] * P1 + c;
}
}
for (int i = A; i <= n; i++)
for (int j = B; j <= m; j++)
insert(h[i][j] - h[i - A][j] * p1[A] - h[i][j - B] * p2[B] + h[i - A][j - B] * p1[A] * p2[B]);
cin >> T;
while (T--)
{
ULL res = 0;
for (int i = 1; i <= A; i++)
{
scanf("%s", s+1);
for (int j = 1; j <= B; j++)
res += s[j] * p1[A - i] * p2[B - j];
}
if (find(res)) cout << 1 << endl;
else cout << 0 << endl;
}
return 0;
}
结语
这是本蒟蒻的第一篇题解,跪求管理员大大给个通过吧!!!如有不当,请各位大佬多多指教!!!!