Description D e s c r i p t i o n
小可可是学校图书馆的管理员,现在他接手了一个十分棘手的任务。 由于学校需要一些材料,校长需要在文章中检索一些信息。校长一共给了小可可 N N 篇文章,每篇文章为一个字符串。现在,校长需要他找到这样的单词,它至少在这 篇文章中的 M M 篇文章里出现过,且单词长度为 。可是,工作量十分庞大,但校长又急需小可可完成这项任务。 现在他向你求助,需要你编写程序完成这项艰巨的任务。
Data Constraint D a t a C o n s t r a i n t
对于 20% 20 % 的数据有 1≤N,M≤10 1 ≤ N , M ≤ 10
对于 60% 60 % 的数据有 1≤N,M≤100 1 ≤ N , M ≤ 100
对于 100% 100 % 的数据有 1≤N,M≤2000,L≤1000 1 ≤ N , M ≤ 2000 , L ≤ 1000
每篇文章长度不大于 1000 1000 ,均有小写字母组成。
Solution S o l u t i o n
想要拿满分就要用字符串哈希,只于你想拿个部分分就像我一样在考场上写个 Trie T r i e 好了。
字符串哈希一般都是二重哈希。有几个步骤叙述一下:
- 我们先把整个字符串看成一个 27 27 进制数(其实更高也可以,不过 27 27 已经够了,主要目的是把每个字母都看成一个别的进制下的“数“),先开出一张 27 27 的幂次的表来以便转换。
- 假设字符串的长度为 L L ,则我们要对 个长度为 l l 的字符串进行字符串哈希。一个一个弄过来复杂度是 明显时间不够,那我们就观察一下,假设这个字符串是 a1a2a3⋯aL a 1 a 2 a 3 ⋯ a L 。我们截取的一段的最后一个一个字母是 ap a p ,则我们截取的字符串是 ap−l+1⋯ap−1ap a p − l + 1 ⋯ a p − 1 a p 。由于这个数拿出来就是 P=ap∗270+ap−1∗271+⋯+ap−l+1∗27l−1 P = a p ∗ 27 0 + a p − 1 ∗ 27 1 + ⋯ + a p − l + 1 ∗ 27 l − 1 。考虑到字符串 a1a2⋯ap−l a 1 a 2 ⋯ a p − l 的值是 M=ap−l∗270+ap−l−1∗271+⋯+a1∗27p−l−1 M = a p − l ∗ 27 0 + a p − l − 1 ∗ 27 1 + ⋯ + a 1 ∗ 27 p − l − 1 ,又考虑到字符串 a1a2⋯ap a 1 a 2 ⋯ a p 的值是 N=ap∗270+ap−1∗271+⋯+a1∗27p−1 N = a p ∗ 27 0 + a p − 1 ∗ 27 1 + ⋯ + a 1 ∗ 27 p − 1 。 我们就可以观察到 M∗27l+P=N M ∗ 27 l + P = N 即 P=N−M∗27l P = N − M ∗ 27 l 。由此,我们就可用一种类似前缀和的方式就可以在 O(L−l+1) O ( L − l + 1 ) 的时间内把所有的值全部计算出来计算出来。
- 有了上面的值,我们就可以进行第一次哈希,由于题目的原因,一个串里面有很多一模一样的子串是不可以重复计算的,所以第一次不可直接累加上去。
- 接下来,就对那些这个串中存在的哈希值进行第二次哈希将其并入总表,这次就需要累加计数。
- 现在,二重哈希的过程就结束了,现在要做的就是遍历哈希总表,统计答案。
Code C o d e
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
#define BASE 27
#define MAXN 2005
#define MAXL 1005
#define MAXSIZE 10003
#define MAXSIZE_DOUBLE 10000007
#define P 1000000003
LL table[MAXSIZE], table_double[MAXSIZE_DOUBLE][2];
LL pow[MAXL], sum[MAXN];
char word[MAXN];
void hash(LL value) {
LL key = value % MAXSIZE;
while (table[key] && table[key] != value)
key = (key + 1) % MAXSIZE;
table[key] = value;
}
void hash_double(LL value) {
LL key = value % MAXSIZE_DOUBLE;
while (table_double[key][0] && table_double[key][0] != value)
key = (key + 1) % MAXSIZE_DOUBLE;
table_double[key][0] = value;
table_double[key][1]++;
}
int main() {
int n, m, l;
scanf("%d%d%d", &n, &m, &l);
pow[0] = 1;
for (int i = 1; i < MAXL; i++)
pow[i] = pow[i - 1] * BASE % P;
for (int i = 1; i <= n; i++) {
memset(table, 0, sizeof table);
scanf("%s", word + 1);
int len = strlen(word + 1);
for (int j = 1; j <= len; j++)
sum[j] = (sum[j - 1] * BASE + (word[j] - 'a')) % P;
for (int j = l; j <= len; j++)
hash((sum[j] - sum[j - l] * pow[l] % P + P) % P);
for (int j = 1; j < MAXSIZE; j++)
if (table[j])
hash_double(table[j]);
}
LL ans = 0;
for (int i = 1; i < MAXSIZE_DOUBLE; i++)
if (table_double[i][1] >= m)
ans++;
printf("%lld\n", ans);
return 0;
}