[洛谷P4052][JSOI2007]文本生成器

题目大意:有$n$个字符串$s_i$,问有多少个长度为$m$的字符串至少包含$n$个字符串中的一个,字符集 A-Z 。$s_i,m\leqslant100,n\leqslant60$

题解:$AC$自动机上$DP$,转换问题为求有多少个长度为$m$的字符串不包含$n$个字符串中的任意一个。定义$f[i][j]$表示现在字符串长度为$i$,匹配到了$AC$自动机上的点$j$且没有出现$n$个字符串中的任意一个的方案数,发现$f[i][j]$可转移到$f[i+1][nxt[j][k]]$。注意,要求$nxt[j][k]$不能是一个串的结尾。可以发现若$nxt[j][k]$的$fail$中没有串的结尾就是合法的,这一个可以在求$fail$时顺带求出

卡点:

 

C++ Code:

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <queue>
const int maxn = 110 * 60, mod = 10007;
inline void reduce(int &x) { x += x >> 31 & mod; }

int n, m, ans = 1;

namespace AC {
	int nxt[maxn][26], fail[maxn], idx = 1;
	bool End[maxn];
	void insert(std::string s) {
		int p = 1;
		for (char ch : s) {
			if (nxt[p][ch - 'A']) p = nxt[p][ch - 'A'];
			else p = nxt[p][ch - 'A'] = ++idx;
		}
		End[p] = true;
	}
	void build() {
		static std::queue<int> q;
		for (int i = 0; i < 26; ++i)
			if (nxt[1][i]) fail[nxt[1][i]] = 1, q.push(nxt[1][i]);
			else nxt[1][i] = 1;
		while (!q.empty()) {
			int u = q.front(); q.pop();
			for (int i = 0; i < 26; ++i)
				if (nxt[u][i]) {
					fail[nxt[u][i]] = nxt[fail[u]][i];
					End[nxt[u][i]] |= End[fail[nxt[u][i]]];
					q.push(nxt[u][i]);
				} else nxt[u][i] = nxt[fail[u]][i];
		}
	}

	void solve() {
		static std::queue<int> q[2];
		int f[111][maxn], tg[maxn];
		int now = 1, pst = 0, u;
		q[now].push(1);
		f[0][1] = 1;
		for (int i = 0; i < m; ++i) {
			std::swap(now, pst);
			while (!q[pst].empty()) {
				u = q[pst].front(), q[pst].pop();
				for (int j = 0; j < 26; ++j)
					if (!End[nxt[u][j]]) {
						reduce(f[i + 1][nxt[u][j]] += f[i][u] - mod);
						if (tg[nxt[u][j]] != i + 1) q[now].push(nxt[u][j]);
						tg[nxt[u][j]] = i + 1;
					}
			}
		}
		for (; !q[now].empty(); q[now].pop())
			reduce(ans -= f[m][q[now].front()]);
	}
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	std::cin >> n >> m;
	for (int i = 1; i <= m; ++i) ans = ans * 26 % mod;
	for (int i = 0; i < n; ++i) {
		static std::string s;
		std::cin >> s;
		AC::insert(s);
	}
	AC::build(), AC::solve();
	std::cout << ans << '\n';
	return 0;
}

  

posted @ 2019-08-05 19:43  Memory_of_winter  阅读(146)  评论(0编辑  收藏  举报