小可可是学校图书馆的管理员,现在他接手了一个十分棘手的任务。 由于学校需要一些材料,校长需要在文章中检索一些信息。校长一共给了小可可 篇文章,每篇文章为一个字符串。现在,校长需要他找到这样的单词,它至少在这 篇文章中的 篇文章里出现过,且单词长度为 。可是,工作量十分庞大,但校长又急需小可可完成这项任务。 现在他向你求助,需要你编写程序完成这项艰巨的任务。
对于 的数据有
对于 的数据有
对于 的数据有
每篇文章长度不大于 ,均有小写字母组成。
想要拿满分就要用字符串哈希,只于你想拿个部分分就像我一样在考场上写个 好了。
字符串哈希一般都是二重哈希。有几个步骤叙述一下:
- 我们先把整个字符串看成一个 进制数(其实更高也可以,不过 已经够了,主要目的是把每个字母都看成一个别的进制下的“数“),先开出一张 的幂次的表来以便转换。
- 假设字符串的长度为 ,则我们要对 个长度为 的字符串进行字符串哈希。一个一个弄过来复杂度是 明显时间不够,那我们就观察一下,假设这个字符串是 。我们截取的一段的最后一个一个字母是 ,则我们截取的字符串是 。由于这个数拿出来就是 。考虑到字符串 的值是 ,又考虑到字符串 的值是 。 我们就可以观察到 即 。由此,我们就可用一种类似前缀和的方式就可以在 的时间内把所有的值全部计算出来计算出来。
- 有了上面的值,我们就可以进行第一次哈希,由于题目的原因,一个串里面有很多一模一样的子串是不可以重复计算的,所以第一次不可直接累加上去。
- 接下来,就对那些这个串中存在的哈希值进行第二次哈希将其并入总表,这次就需要累加计数。
- 现在,二重哈希的过程就结束了,现在要做的就是遍历哈希总表,统计答案。
#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;
}