[JSOI2007]文本生成器

传送门

Analysis

AC自动机+dp
直接从正面做
(f[i][j][0/1])表示在节点(i),串长为(j),是否已经经过结尾点的总方案数,然后从父亲向儿子转移
(dp)的时候不用跳(fail),在构建(fail)指针的时候顺带把对于结尾点的标记通过(fail)指针扩展到它在(fail)树上的祖先(不知道这样理解对不对)

ps:大写看成小写调了1h……

放代码

#include<bits/stdc++.h>
using namespace std;
const int N = 10000 + 10;
const int mod = 10007;
char s[N];
int n, m, ch[N][30], lst[N], fail[N], val[N], f[N][105][2], sz = 1, ans;
void insert(char *s) {
	int u = 0; int n = strlen(s);
	for (register int i = 0; i < n; ++i) {
		int c = s[i] - 'A';
		if (!ch[u][c]) {
			memset(ch[sz], 0, sizeof ch[sz]);
			val[sz] = 0;
			ch[u][c] = sz++;
//			cout << u << " " << c << ' ' << ch[u][c] << endl;
		} u = ch[u][c];
	} val[u] = 1;
}
void getfail() {
	queue <int> q;
	int u = 0; fail[0] = 0;
	for (register int i = 0; i < 26; ++i) 
		if (ch[0][i]) q.push(ch[0][i]), fail[ch[0][i]] = lst[ch[0][i]] = 0;
	while (!q.empty()) {
		int r = q.front(); q.pop();
		for (register int i = 0; i < 26; ++i) {
			u = ch[r][i];
			if (!ch[r][i]) {
				ch[r][i] = ch[fail[r]][i];
				continue;
			} q.push(u);
			int v = fail[r];
			while (v && !ch[v][i]) v = fail[v];
			fail[u] = ch[v][i];
			val[u] |= val[fail[u]];
			lst[u] = val[fail[u]] ? fail[u] : lst[fail[u]]; 
		}
	}
}
void dp() {
	f[0][0][0] = 1;
	for (register int i = 1; i <= m; ++i) 
		for (register int j = 0; j < sz; ++j) 
			for (register int k = 0; k < 26; ++k) {
				int u = ch[j][k];
//				cout << u << ' ' << i << ' ' << j << endl;
				if (val[u]) f[u][i][1] = (f[u][i][1] + f[j][i - 1][0] + f[j][i - 1][1]) % mod;
				else {
					f[u][i][0] = (f[u][i][0] + f[j][i - 1][0]) % mod;
					f[u][i][1] = (f[u][i][1] + f[j][i - 1][1]) % mod;
				}
			}
}
int main() {
	cin >> n >> m;
	for (register int i = 1; i <= n; ++i) {scanf("%s", s); insert(s);}
	getfail(); 
	dp();
	for (register int i = 0; i < sz; ++i) ans = (ans + f[i][m][1]) % mod;
	cout << ans;
	return 0;
}