本来想用\(trie\)的,结果有一个神奇的数组赋值无效……
思路:先求出第一问答案,可以\(O(n)\)一遍扫出来,注意背诵的单词只统计一次
难点在于第二问。
可以用单调队列扫一下,记录队列中每个单词在文章中的位置,对于队头,如果这里的单词在后面出现过,队头就可以不要了。
当队伍中单词达到数量时,可以统计答案了。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<string>
#include<map>
using namespace std;
int head=1,tail,n,m,cnt,ans;
int tot,q[100010],a[100010];
int b[100010],vis[100010];
char ch[21];
map<string,int>mp;
const int inf=2147483647;
int main(){
ans=inf;
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%s",ch);
mp[ch]=i;
}
scanf("%d",&m);
for(int i=1;i<=m;++i){
scanf("%s",ch);
a[i]=mp[ch];
if(!a[i])continue;
if(!vis[a[i]]){
vis[a[i]]=1;
tot++;
}//找出现过的所有单词
//也可以用trie
}
if(!tot){//特判,因为tail-head最小是1,不特判WA一个点
printf("0\n0");
return 0;
}
for(int i=1;i<=m;++i){
if(!b[a[i]]&&a[i])cnt++;
b[a[i]]=i,q[++tail]=i;
//b数组存的是当前要背单词在队列中最靠后的位置
//q数组存的是当前队列中有的要背单词的文章中pos
//如果当前队列里面有值并且当前队头的单词在后面出现过
//可以去掉队头,指针后移
//当队伍中已经包含所有单词时
//更新答案
while(head<=tail&&q[head]<b[a[q[head]]])head++;
if(cnt==tot)ans=min(ans,tail-head+1);
}
printf("%d\n%d",tot,ans);
return 0;
}
原文地址:https://www.cnblogs.com/h-lka/p/12179563.html
时间: 2024-11-06 17:07:03