題目描述
靈夢有n個單詞想要背,但她想通過一篇文章中的一段來記住這些單詞。
文章由m個單詞構成,她想在文章中找出連續的一段,其中包含最多的她想要背的單詞(重複的只算一個)。並且在背誦的單詞量儘量多的情況下,還要使選出的文章段落儘量短,這樣她就可以用盡量短的時間學習儘可能多的單詞了。
輸入格式
第1行一個數n,
接下來n行每行是一個長度不超過10的字符串,表示一個要背的單詞。
接着是一個數m,
然後是m行長度不超過10的字符串,每個表示文章中的一個單詞。
輸出格式
輸出文件共2行。第1行爲文章中最多包含的要背的單詞數,第2行表示在文章中包含最多要背單詞的最短的連續段的長度。
輸入:
3
hot
dog
milk
5
hot
dog
dog
milk
hot
輸出:
3
3
說明/提示
【數據範圍】
對於30%的數據 n<=50,m<=500;
對於60%的數據 n<=300,m<=5000;
對於100%的數據 n<=1000,m<=100000;
中文題目不解釋題意~
思路:
- 字符串匹配直接想到hash,用hash完全可以解決第一個問題;
- 本題的關鍵在第二問,找最短的含有最大單詞數的連續段長度;暴力沒想到怎麼暴力,然後想到可以用尺取,滑動尋找,但是寫了半天wa了一發,自己找到反例,應該是尺取的本事還沒到家~~
- 後來看到有用隊列A的,瞬間又有了思路,所有解釋都在無敵詳細的註釋中!!!
代碼:
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define H 0x7FFFFFFF
using namespace std;
const int maxn=1e5+7;
const ll INF=1e9+7;
char s[20];
map<ull,int>mp;
ull gethash(char *a)
{
ull sum=0;
for(int i=0;a[i]!='\0';i++)
{
sum=sum*131+a[i];
}
return sum&H;
}
map<ull,int>mp2;
char a[maxn][15];
int main()
{
ll n,m;
scanf("%lld",&n);
for(int i=1;i<=n;i++)
{
scanf("%s",s);
mp[gethash(s)]=1;
}
scanf("%lld",&m);
ll num=0;
for(int i=1;i<=m;i++)
{
scanf("%s",a[i]);
ull x=gethash(a[i]);
if(mp[x]==1) //hash解決第一問
{
num++;
mp[x]++;
}
}
printf("%lld\n",num);
queue<int>q;
bool flag=false;
ll ans=INF;
for(int i=1;i<=m;i++)
{
if(mp[gethash(a[i])]==0)
mp2[gethash(a[i])]=INF;
/*
這裏要將文章中出現的但不需要背的賦爲最大,順着往後理解就能懂
否則過不了下面註釋的例子
因爲這裏卡了很久
*/
flag=false; //不要忘了初始化
if(!mp2[gethash(a[i])])
flag=true;
/*
這裏當該單詞需要背,並且文章中沒有出現過時,ans爲隊列長度;否則爲ans與隊列長度取最小;
取最小是因爲隊首元素可能會被去掉,在下面的操作中
這裏要分開討論的原因爲這個例子:3 a b c 5 a b a a a ;這樣處理後面的三個a就不會被計算
*/
q.push(i);
mp2[gethash(a[i])]++;
int k=q.front();
while(mp2[gethash(a[k])]>1&&q.size()) //當隊首元素出現過>1次時, 去掉該元素;
{
mp2[gethash(a[k])]--;
q.pop();
k=q.front();
}
ll t=q.size();
if(flag)
ans=t;
else
ans=min(t,ans);
}
printf("%lld\n",ans);
return 0;
}
/*
3
a
b
c
5
a
b
e
a
a
*/