2020.6.13
声明本文借用了@hyfhaha 的题解思想。
前天回学校看了趟老师,都还是那个样子,一点没变。老师见到我们也很高兴,把我们留到了晚自习下课才放走。A-Level的班问cs的准备工作我还小小地安利了一下noip,嘻嘻,希望能够给学弟学妹一点准备。今年这个走势确实不太好,看到谭老师对下届忧心忡忡的样子真的很难过。一方面是大环境,一方面是,七中从我们这届之后录取结果就有点差强人意了,虽然这届总有几个20-30的,但是我们那届可是整个班一半人都到了美国前30大学的,没有前30的专业也都是最顶尖的,方的bu生物工程,wsl的艺术学院,lyx的动物医学都是顶尖专业。可惜了,老师们也经常说18届毕业之后再也见不到我们这样的学生了,可能跟机构有关,可能跟人有关,我们那届进来的人像我这样曲折的并不在少数,也可能跟不断贬值的sat分数有关,不知道什么原因,还是希望学校能迎接下一段辉煌,有了更好的结果,我们的退出也更有意义吧。
ac自动机,这道题的白板思路就是暴力匹配,但是看到1e6,多组数据,我认为100%超时,然后有人告诉我这个可以在fail树上操作,还是没想出来。看了题解才知道原来是可以边匹配边搞,就是对于trie[cur][val]每一个都有唯一确定下标的子串,使得在匹配a的时候如果包含了b在路径上,b必为a的子串,只要将下标所代表的trie的结束节点标上下标,匹配的时候直接加就可以了,很巧妙的办法。本来想用char数组的,然而发现开不下,还是算了,用str吧。
代码:
#include <bits/stdc++.h>
using namespace std;
#define limit (500000 + 5)//防止溢出
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f
#define lowbit(i) i&(-i)//一步两步
#define EPS 1e-6
#define FASTIO ios::sync_with_stdio(false);cin.tie(0);
#define ff(a) printf("%d\n",a );
#define pi(a,b) pair<a,b>
#define rep(i, a, b) for(int i = a; i <= b ; ++i)
#define per(i, a, b) for(int i = b ; i >= a ; --i)
#define mint(a,b,c) min(min(a,b), c)
#define MOD 998244353
#define FOPEN freopen("C:\\Users\\tiany\\CLionProjects\\acm_01\\data.txt", "rt", stdin)
typedef long long ll;
typedef unsigned long long ull;
ll read(){
ll sign = 1, x = 0;char s = getchar();
while(s > '9' || s < '0' ){if(s == '-')sign = -1;s = getchar();}
while(s >= '0' && s <= '9'){x = x * 10 + s - '0';s = getchar();}
return x * sign;
}//快读
void write(ll x){
if(x < 0) putchar('-'),x = -x;
if(x / 10) write(x / 10);
putchar(x % 10 + '0');
}
int n,num[limit];
int ans[limit];
struct node{
#define ALPHA 25
int trie[limit][26], fail[limit],tot = 0;
void clear(){
memset(trie, 0 , sizeof(trie));
memset(fail, 0 , sizeof(fail));
memset(num, 0 , sizeof(num));
memset(ans, 0 , sizeof(ans));
tot = 0;
}
void insert(const char *s, int index){
int root = 0;
int len = strlen(s);
rep(i , 0 ,len - 1){
int val = s[i] - 'a';
if(!trie[root][val])trie[root][val] = ++tot;//没有被访问过
root = trie[root][val];
}
num[root] = index;
}
void build(){
queue<int>q;
rep(i ,0 , ALPHA){
if(trie[0][i])fail[trie[0][i]] = 0, q.push(trie[0][i]);
}
while (q.size()){
int u = q.front();
q.pop();
rep(i , 0 ,ALPHA){
if(trie[u][i]){
fail[trie[u][i]] = trie[fail[u]][i];
q.push(trie[u][i]);
}else{
trie[u][i] = trie[fail[u]][i];
}
}
}
}
void operator[](const char * s){
int len = strlen(s);
int root = 0 ;
rep(i, 0 , len - 1){
root = trie[root][s[i] - 'a'];
for(int t = root; t ; t = fail[t]){
++ans[num[t]];
}
}
}
};
string str;
string collection[limit];
node AC_Automaton;//命名
int main() {
#ifdef LOCAL
FOPEN;
#endif
while(cin>>n && n){
rep(i ,1, n){
cin>>str;
collection[i] = str;
AC_Automaton.insert(str.c_str(), i);
}
int maxx = 0;
cin>>str;
AC_Automaton.build();
AC_Automaton[str.c_str()];
rep(i ,1, n){
maxx = max(maxx, ans[i]);
}
write(maxx),putchar('\n');
rep(i,1,n){
if(ans[i] == maxx){
cout<<collection[i]<<endl;
}
}
AC_Automaton.clear();
}
return 0;
}