題目鏈接:https://www.luogu.org/problem/P3796
這題相比簡單版就稍微調整一下,用ans數組存儲單詞出現的次數,然後最後比較次數大小輸出次數最多的那幾個字符串就行了,注意的是這題不用記錄點是否被訪問過,因爲要統計出現的次數。
#include<iostream>
#include<cstring>
#include<string>
#include<queue>
using namespace std;
const int maxn=1e6+10;
int cnt=0;
int ans[maxn];
int trie[maxn][26];//trie[i][j]表示的當前字母爲i+'a',且父節點編號爲i的節點的編號
int cntword[maxn];
int fail[maxn];
void insert(string s,int u)
{
int root=0;
for(int i=0;i<s.size();i++)
{
int next=s[i]-'a';
if(!trie[root][next])
trie[root][next]=++cnt;
root=trie[root][next];
}
cntword[root]=u;//當前節點的單詞數+1,而不是等於1,因爲可能重複。
//這時的root是單詞的末尾的編號,表示的是一整個單詞,途中經歷過的點的cntword是沒有自增的;
}
void getFail()//層序遍歷
{
queue<int>q;
for(int i=0;i<26;i++)
if(trie[0][i])//首先要先將最上面一層出現的字母(每個串的首字符)入隊
{
fail[trie[0][i]]=0;
q.push(trie[0][i]);
}
while(!q.empty())
{
int now=q.front();
q.pop();
for(int i=0;i<26;i++)
{
if(trie[now][i])//通過枚舉26個字母來找到now的子節點們
{
//該節點i的失敗指針指向它父節點now的失敗指針fail[now]的和i節點相同的節點
fail[trie[now][i]]=trie[fail[now]][i];
q.push(trie[now][i]);
}
else
trie[now][i]=trie[fail[now]][i];//如果不存在這個節點就上跳到失配節點的等於i的這個子節點上
}
}
}
void query(string s)
{
int now=0;
for(int i=0;i<s.size();i++)
{
now=trie[now][s[i]-'a'];
for(int j=now;j&&cntword[j]!=-1;j=fail[j])//當前節點匹配完成或失配時就上跳fail指針
{
ans[cntword[j]]++;//只有j是某個單詞末尾時纔有值
//cntword[j]=-1;//記錄過的節點要標記,防止重複
}
}
}
int main()
{
int n;
while(cin>>n&&n)
{
cnt=0;
memset(cntword,0,sizeof(cntword));
memset(fail,0,sizeof(fail));
memset(trie,0,sizeof(trie));
memset(ans,0,sizeof(ans));
string a[155],s;
for(int i=1;i<=n;i++)
{
cin>>a[i];
insert(a[i],i);
}
getFail();
cin>>s;
query(s);
int maxnum=-0x7fffff;
for(int i=1;i<=n;i++)
maxnum=max(maxnum,ans[i]);
cout<<maxnum<<endl;
for(int i=1;i<=n;i++)
if(ans[i]==maxnum)
cout<<a[i]<<endl;
}
return 0;
}