/*
Copyright 2013 Marek "p2004a" Rusinowski
Aho-Corasick algorithm
*/
#include <cstdio>
#include <vector>
#include <string>
#include <deque>
#define MAXN 1000000
using namespace std;
class PatternsMatcher {
class TrieNode {
public:
static int it;
TrieNode *son[26];
int num;
TrieNode *fail;
TrieNode() : num(0), fail(NULL) {
for (int i = 0; i < 26; ++i) son[i] = NULL;
}
TrieNode(const char *str) : num(0), fail(NULL) {
for (int i = 0; i < 26; ++i) son[i] = NULL;
if (*str != '\0') son[*str - 'a'] = new TrieNode(str + 1);
else num = ++it;
}
~TrieNode() {
for (int i = 0; i < 26; ++i) delete son[i];
}
void add(const char *str) {
if (*str == '\0') num = ++it;
else if (son[*str - 'a'] == NULL) son[*str - 'a'] = new TrieNode(str + 1);
else son[*str - 'a']->add(str + 1);
}
};
TrieNode root;
bool fail_ok;
void compute_fail() {
root.fail = &root;
deque<TrieNode*> q;
q.push_back(&root);
while (!q.empty()) {
TrieNode *v = q.front();
q.pop_front();
for (int i = 0; i < 26; ++i) {
if (v->son[i] != NULL) {
q.push_back(v->son[i]);
TrieNode *tmp = v->fail;
while (tmp != &root && tmp->son[i] == NULL) tmp = tmp->fail;
v->son[i]->fail = tmp->son[i] != NULL && tmp->son[i] != v->son[i] ? tmp->son[i] : &root;
}
}
}
}
public:
PatternsMatcher() : fail_ok(false) {}
void add_pattern(const char *str) {
root.add(str);
fail_ok = false;
}
void search(const char *str, bool (*callback)(int idx, int pat_idx, void *data), void *data = NULL) {
if (!fail_ok) compute_fail();
fail_ok = true;
TrieNode *v = &root;
bool stop = false;
for (int i = 0; *str != '\0' && !stop; ++i, ++str) {
int j = *str - 'a';
while (v != &root && v->son[j] == NULL) v = v->fail;
v = v->son[j] != NULL ? v->son[j] : &root;
for (TrieNode *u = v; u != &root; u = u->fail) {
if (u->num > 0 && !callback(i, u->num - 1, data)) {
stop = true;
break;
}
}
}
}
/*
This function creates an input for Graphviz dot representing a Trie structure.
Useful while debugging. Maybe it will be useful while hacking the code
void print(FILE *file) {
deque<deque<TrieNode *> > rank;
rank.push_back(deque<TrieNode *>());
rank[0].push_back(&root);
deque<pair<TrieNode*, unsigned> > q;
q.push_back(make_pair(&root, 0));
fprintf(file, "digraph trie {\n");
while (!q.empty()) {
pair<TrieNode*, unsigned> p = q.front();
q.pop_front();
if (p.first->fail != NULL) {
fprintf(file, " \"%p\" -> \"%p\" [style=\"dashed\"];\n", p.first, p.first->fail);
}
for (unsigned i = 0; i < 26; ++i) {
if (p.first->son[i] != NULL) {
q.push_back(make_pair(p.first->son[i], p.second + 1));
if (rank.size() == p.second + 1) rank.push_back(deque<TrieNode *>());
rank.back().push_back(p.first->son[i]);
fprintf(file, " \"%p\" -> \"%p\" [label=\" %c\"];\n", p.first, p.first->son[i], i + 'a');
}
}
}
for (unsigned i = 0; i < rank.size(); ++i) {
fprintf(file, " {rank=same; ");
for (unsigned j = 0; j < rank[i].size(); ++j) {
fprintf(file, "\"%p\" ", rank[i][j]);
}
fprintf(file, "}\n");
}
fprintf(file, "}\n");
fflush(file);
}*/
};
int PatternsMatcher::TrieNode::it = 0;
bool f(int i, int idx, void *data) {
vector<string> &t = *((vector<string> *) data);
printf("%s%s\n", string(i - t[idx].size() + 1, ' ').c_str(), t[idx].c_str());
return true;
}
int main() {
char *str = new char [MAXN];
int n;
scanf("%d\n", &n);
PatternsMatcher x;
vector<string> t;
for (int i = 0; i < n; ++i) {
scanf("%s\n", str);
x.add_pattern(str);
t.push_back(str);
}
scanf("%s\n", str);
printf("%s\n", str);
x.search(str, f, &t);
delete str;
return 0;
}