看了一下简单的$Trie图$,调模板调啊调一连调了$2h$,最后发现$-'a'$打成$-'A'$了hhh,有种摔键盘的冲动。
$Trie图$是$Trie树$上建立“前缀边”,不用再像在$Trie树$上那样顺着$fail$一个一个往上跳了,省了不少时间。这种做法在$hihoCoder$上时间排到了前三名。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1000006
using namespace std;
int c[N][26], cnt = 0, fail[N], n, q[N], w[N];
inline void ins(char *s) {
int len = strlen(s), now = 0;
for(int i = 0; i < len; ++i) {
int t = s[i] - 'a';
if (!c[now][t]) c[now][t] = ++cnt;
now = c[now][t];
}
w[now] = 1;
}
inline void BFS() {
int now, head = -1, tail = -1;
for(int t = 0; t < 26; ++t)
if (c[0][t])
q[++tail] = c[0][t];
while (head != tail) {
now = q[++head];
for(int t = 0; t < 26; ++t)
if (!c[now][t])
c[now][t] = c[fail[now]][t]; //建立“前缀边”
else {
q[++tail] = c[now][t];
int tmp = fail[now];
while(tmp && !c[tmp][t])
tmp = fail[tmp];
fail[c[now][t]] = c[tmp][t];
}
}
}
inline void AC(char *s) {
int len = strlen(s), now = 0;
for(int i = 0; i < len; ++i) {
now = c[now][s[i] - 'a'];
if (w[now]) {
puts("YES");
return;
}
}
puts("NO");
}
int main() {
scanf("%d\n", &n);
char s[N];
for(int i = 1; i <= n; ++i)
scanf("%s", s), ins(s);
BFS();
scanf("%s", s);
AC(s);
return 0;
}
不要介意“前缀边”这个名字起得多么牵强,可以理解为记录$fail$最终跳到的点,直接指过去就行了。gty学长讲课时也讲过这种优化。