【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学长讲课时也讲过这种优化。

posted @ 2016-04-05 16:11  abclzr  阅读(322)  评论(0编辑  收藏  举报