【Description】
灵梦有n个单词想要背,但她想通过一篇文章中的一段来记住这些单词。
文章由m个单词构成,她想在文章中找出连续的一段,其中包含最多的她想要背的单词(重复的只算一个)。并且在背诵的单词量尽量多的情况下,还要使选出的文章段落尽量短,这样她就可以用尽量短的时间学习尽可能多的单词了。。
【Input】
第1行一个数n,
接下来n行每行是一个长度不超过10的字符串,表示一个要背的单词。
接着是一个数m,
然后是m行长度不超过10的字符串,每个表示文章中的一个单词。
【Output】
输出文件共2行。第1行为文章中最多包含的要背的单词数,第2行表示在文章中包含最多要背单词的最短的连续段的长度。
【Sample Input】
3
hot
dog
milk
5
hot
dog
dog
milk
hot
【Sample Output】
3
3
【Data Size and Hint】
对于30%的数据 n<=50,m<=500;
对于60%的数据 n<=300,m<=5000;
对于100%的数据 n<=1000,m<=100000;
【Solution】
数据比较大,因此储存最优方式是哈希表。
判断文章中单词与哈希表中相同的个数,就解决了第一问。
第二问的方式很久没用这个思想,一时也没想出来,看了别人题解,方法就是一个l,一个r记录查找范围,范围中至少最多包含的单词每个都出现一次,根据这个思想就可以在
O(m)
的时间内解决。
const mode=100007;
var n,m,ans,minl:longint;
p,num:array[0..mode] of longint;
table,used:array[0..mode] of boolean;
str:array[0..mode] of string;
function hash(s:string):longint; //哈希
var i,tmp:longint;
begin
tmp:=1; hash:=0;
for i:=1 to length(s) do begin
hash:=(hash+ord(s[i])*tmp) mod mode;
tmp:=(tmp*31) mod mode;
end;
while (table[hash]) and (str[hash]<>s) do hash:=(hash+1) mod mode;
exit(hash);
end;
procedure init; //读入要背的单词
var s:string;
i:longint;
begin
readln(n);
for i:=1 to n do begin
readln(s);
str[hash(s)]:=s;
table[hash(s)]:=true;
end;
end;
procedure main;
var i,j,l,r,tot:longint;
s:string;
begin
readln(m); fillchar(used,sizeof(used),false);
for i:=1 to m do begin //读入文章,处理最大值
readln(s);
p[i]:=hash(s);
if table[p[i]] and (not used[p[i]]) then begin
inc(ans); used[p[i]]:=true;
end;
end;
writeln(ans);
//找到最短包含最大值的长度
l:=1; r:=1; tot:=0; minl:=maxlongint;
fillchar(num,sizeof(num),0);
while (r<=m) do begin
while (r<=m) do begin
if (used[p[r]]) and (num[p[r]]=0) then inc(tot);
inc(num[p[r]]);
inc(r);
if tot=ans then break;
end;
while ((not used[p[l]]) or (num[p[l]]>1)) and (l<r) do begin
dec(num[p[l]]); inc(l);
end;
if r-l<minl then minl:=r-l;
end;
writeln(minl);
end;
begin
init;
main;
end.