题目
给你一些单词,再给你一段文章。
要你求此文章中包含多少个给出的单词。
再求文章中的一段,使之包含 给出的单词最多(不计重),输出其最小的长度。
解
字符串哈希,然后尺取法。
尺取法通常是指对数组保存一对下标(起点,终点),然后根据实际情况交替推进两个端点直到得出答案的方法,这种操作很像是尺取虫爬行的方式故得名。
双重哈希防哈希值重复。
代码
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int ls, flag, ans1, ans2, lans, n, t, l[100010], k1[100010], k2[100010];
bool b[100010];
string s[100010], ss;
struct asdf{
int z, next;
} a[100010];
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; ++i){
//读入单词
cin >> ss;
ls = ss.size();
int kk1 = 0, kk2 = 0;
for(int j = 0; j < ls; ++j)
kk1 = (kk1 * 13331 + ss[j]) % 10003;
for(int j = 0; j < ls; ++j)
kk2 = (kk2 * 10007 + ss[j]) % 10003;
a[++t] = (asdf){
kk2, l[kk1]}; l[kk1] = t; //邻接表储存
}
scanf("%d", &n);
ans2 = n;
for(int i = 1; i <= n; ++i){
//读入文章单词
cin >> s[i];
ls = s[i].size();
for(int j = 0; j < ls; ++j) //计算其哈希值
k1[i] = (k1[i] * 13331 + s[i][j]) % 10003;
for(int j = 0; j < ls; ++j)
k2[i] = (k2[i] * 10007 + s[i][j]) % 10003;
for(int j = l[k1[i]]; j; j = a[j].next) //判断是否是要背诵的单词
if(a[j].z == k2[i] && b[j] == 0){
b[j] = 1;
++ans1; //是。统计入答案
break;
}
}
for(int i = 1; i <= n - ans1 + 1; ++i){
//尺取法
memset(b, 0, sizeof(b));
lans = 0;
for(int j = i; j <= n; ++j){
for(int k = l[k1[j]]; k; k = a[k].next)
if(a[k].z == k2[j]){
//如果这个单词是要背的
if(b[k] == 0){
//取的这段中没出现过
b[k] = 1;
++lans; //计入答案
}
break;
}
if(lans == ans1) {
//如果背完所需的了
ans2 = min(ans2, j - i + 1); //取最小长度
break;
}
}
}
printf("%d\n%d", ans1, ans2); //输出答案
}