BZOJ1030 [JSOI2007]文本生成器【AC自动机+DP】

题意:给定n个字符串,要求构造长度为m的字符串使其包含至少一个给定字符串,求方案数。

AC自动机+DP肯定是显然的啦。但是要求“至少一个”被包含肯定不好搞,所以我们反过来想,用总方案数-一个也不包含的方案数这样就好搞多了。具体算法流程如下:

1.对n个字符串建立AC自动机;

2.DP,f[i][j]表示字符串构造到第i位,在AC自动机上跑到j节点时的方案数。转移方程:(如果tr[j][k]不是一个字符串的末尾)f[i+1][tr[j][k]]=(f[i+1][tr[j][k]]+f[i][j])%MOD,其中k是26个字母。

3.ans=26^m-∑f[m][i],其中i是所有AC自动机上的节点编号。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<bitset>
#include<queue>
#define ll long long
#define INF 0x3f3f3f3f
#define N 110
#define MOD 10007
using namespace std;
char str[N];
int n;
int tot=1,len,tr[N*180][30],g[N*180];
bool num[180*N];
int f[N][N*180];
queue<int> q;
void add(int x,int y){
	if(y>=len){ num[x]=1; return;}
	int tmp=str[y]-'A';
	if(!tr[x][tmp]) tr[x][tmp]=++tot;
	add(tr[x][tmp],y+1);
}
void bfs(){
	int j,y;
	g[1]=0;
	q.push(1);
	while(q.size()){
		int u=q.front();
		q.pop();
		for(int i=0;i<26;i++){
			for(j=g[u];j;j=g[j]) if(tr[j][i]) break;
			if(y=tr[u][i]){
				g[y]=j ? tr[j][i] : 1;
				num[y]|=num[g[y]];
				q.push(y);
			}
			else tr[u][i]=j ? tr[j][i] : 1;
		}	
	}
}
void dp(){
	f[0][1]=1;
	for(int i=0;i<len;i++){
		for(int j=1;j<=tot;j++){
			for(int k=0;k<26;k++)
				if(!num[tr[j][k]]){
					f[i+1][tr[j][k]]=(f[i][j]+f[i+1][tr[j][k]])%MOD;
				}
		}
	}
}
void work(){
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%s",str);
		len=strlen(str);
		add(1,0);
	}
	len=m;
	bfs();
	dp();
	int ans=1;
	for(int i=1;i<=m;i++) ans=ans*26%MOD;
	for(int i=1;i<=tot;i++) {
		ans=ans-f[len][i];
		if(ans<0) ans+=MOD;
	}
	printf("%d\n",ans);
}
int main(){
	work();
	return 0;
}


猜你喜欢

转载自blog.csdn.net/u011431896/article/details/45241969