题解 LuoguP4052文本生成器(AC自动机 + DP)

题意

https://www.luogu.com.cn/problem/P4052

算法

AC自动机上的套路DP

思路

正面处理困难,于是考虑容斥,求不合法的方案数。考虑使用AC自动机,则原问题转化为:求使这\(m\)个字符串都匹配不上的字符串的个数。所以,匹配过程中经过的点,其\(fail\)链上的节点(包括本身)都不能有结束节点。

\(dp(i,j)\)表示从根节点开始走\(i\)步到\(j\)号节点的方案数,状态转移方程为:

\[dp(i, j) = \sum_{!t[k].num}dp(i - 1, k){ (t[k].num表示k号节点的fail链上是否有结束节点)} \]

代码

#include <bits/stdc++.h>

using namespace std;

const int maxn = 5e4 + 10,mode = 1e4 + 7;
int n,f[6010][6010],m,ans;
string a;

struct AC{
	int cnt;
	struct Tree{
		int num,fail;
		int ch[30];
	}t[maxn];
	AC(){cnt = 0;}

	void build(string s){
		int len = s.size(); int now = 0;
		for(int i = 0; i < len; ++ i){
			int c = s[i] - 'A';
			if(!t[now].ch[c]) t[now].ch[c] = ++cnt;
			now = t[now].ch[c];
		}
		t[now].num = 1;
	}

	void get_fail(){
		queue<int> q;
		for(int i = 0; i < 26; ++ i)
			if(t[0].ch[i]){
				t[t[0].ch[i]].fail = 0;
				q.push(t[0].ch[i]);
			}
		while(!q.empty()){
			int u = q.front(); q.pop();
			for(int i = 0; i < 26; ++ i){
				int v = t[u].ch[i];
				if(v){
					t[v].fail = t[t[u].fail].ch[i];
					q.push(v);
					t[v].num |= t[t[v].fail].num;
				}
				else t[u].ch[i] = t[t[u].fail].ch[i];
			}
		}
	}

	void DP(){
		f[0][0] = 1;
		for(int i = 1; i <= m; ++ i)
			for(int j = 0; j <= cnt; ++ j)
				if(!t[j].num){
					for(int k = 0; k < 26; ++ k)
						f[i][t[j].ch[k]] = (f[i][t[j].ch[k]] + f[i - 1][j]) % mode;
				}
		for(int i = 0; i <= cnt; ++ i)
			if(!t[i].num) ans += f[m][i], ans %= mode;
	}
}ac;

int ksm(int a, int b){  //递归版本快速幂
	if(b == 1) return a;
	if(b == 0) return 1;
	if(b & 1) return ((a * ksm(a, b / 2)) % mode * ksm(a, b / 2)) % mode;
	else return (ksm(a, b / 2) * ksm(a, b / 2)) % mode;
}

int main(){
	ios::sync_with_stdio(false);
	cin >> n >> m;
	for(int i = 1; i <= n; ++ i){
		cin >> a;
		ac.build(a);
	}
	ac.get_fail(); ac.DP();
	cout << (ksm(26, m) - ans + mode) % mode << endl;
	return 0;
}
posted @ 2020-10-13 15:02  When_C  阅读(151)  评论(3编辑  收藏  举报