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

本文介绍了一种利用字符串哈希解决大规模文本检索问题的方法。通过将字符串视为特定进制数,采用二重哈希技术高效地查找指定长度的单词,确保这些单词在多篇文章中出现的次数满足条件。
612

被折叠的 条评论
为什么被折叠?



