【BZOJ 1030】【JSOI 2007】文本生成器 AC自动机+递推

一直不理解到底怎么做啊,想了好久$TwT$

最后终于明白了为什么找到第一个满足条件的$fail$就计算,因为避免重复,这个回答,,,

然后$root$下面要接上26个节点,这里26个字母中不在字典内的都用$f[i][0]$代替了,这个也想了好久,$==$我还是回家种地吧。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define mo 10007
#define N 103
#define M 6003
using namespace std;
int q[M], c[M][26], w[M], fail[M], f[N][M], n, m, cnt = 0;
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 head = 0, tail = 1, now = 0;
	q[1] = 0;
	while (head != tail) {
		now = q[++head];
		for(int t = 0; t < 26; ++t)
			if (c[now][t]) {
				q[++tail] = c[now][t];
				if (now == 0) continue;
				int tmp = fail[now];
				while (tmp && !c[tmp][t])
					tmp = fail[tmp];
				fail[c[now][t]] = c[tmp][t];
				if (w[c[tmp][t]])
					w[c[now][t]] = 1; //!!!!!!!
			}
	}
}
inline int ipow(int x, int nn) {
	int ans = 1;
	for(int t = x; nn; nn >>= 1, t = (t * t) % mo)
		if (nn & 1) {
			ans = (ans * t);
			if (ans >= mo)
				ans = ans % mo;
		}
	return ans;
}
inline void AC() {
	f[0][0] = 1;
	for(int i = 0; i <= m; ++i)
		for(int j = 0; j <= cnt; ++j)
			if (f[i][j]) {
				for(int t = 0; t < 26; ++t) {
					int tmp = j;
					while (tmp && !c[tmp][t])
						tmp = fail[tmp];
					tmp = c[tmp][t];
					if (!w[tmp]) {
						f[i + 1][tmp] += f[i][j];
						if (f[i + 1][tmp] >= mo)
							f[i + 1][tmp] %= mo;
					}
				}
			}
	int _ = 0, al = ipow(26, m);
	for(int i = 0; i <= cnt; ++i)
		if (!w[i]) {
			_ += f[m][i];
			if (_ > mo)
				_ %= mo;
		}
	int ans = ((al - _) % mo + mo) %mo;
	printf("%d\n", ans);
}
int main() {
	scanf("%d%d", &n, &m);
	char s[N];
	for(int i = 1; i <= n; ++i)
		scanf("%s", s), ins(s);
	BFS();
	AC();
	return 0;
}

最后终于做出来了,因为看了别人的模板,,,,这,,,

posted @ 2016-04-05 10:10  abclzr  阅读(150)  评论(0编辑  收藏