【hihoCoder 1036】Trie图
看了一下简单的$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学长讲课时也讲过这种优化。
NOI 2017 Bless All