1. 程式人生 > 其它 >題解[ [JSOI2007]文字生成器 ]

題解[ [JSOI2007]文字生成器 ]

題目

Link

給定\(n\)個模式串,求長度為\(m\)且不含模式串的文字串個數。

Sol

學習AC自動機的時候刷到了這道題.

看網上的各種題解,發現這類AC自動機上DP都是一種類似的套路。

首先,設\(f[i][j]\)表示匹配到長度為\(i\)的地方,匹配到AC自動機上的第\(j\)個節點且不含模式串的方案數。

由於我們正向算方案容易算重,所以考慮求不含模式串的文字串數量。

設輔助陣列\(sz[id]\)表示在AC自動機上的第\(id\)個節點之前是否已經含有至少一個模式串。

\(nx[i][j]\)表示AC自動機上第\(i\)個節點,且下一個字元為\(j\)的後繼節點。

那麼:

\[if(!sz[j][k])\ f[i][nx[j][k]]+=f[i-1][j] \]

列舉一下就好了。

Code

#include<bits/stdc++.h>
#define N (61)
#define M (10010)
using namespace std;
const int P=10007;
int n,m,f[110][M];
char ss[M];
inline int read(){
	int w=0;
	char ch=getchar();
	while(ch>'9'||ch<'0') ch=getchar();
	while(ch>='0'&&ch<='9'){
		w=(w<<3)+(w<<1)+(ch^48);
		ch=getchar();
	}
	return w;
}
struct Aho_C{
	int tot,nx[M][26],sz[M],fail[M];
	inline void insert(){
		scanf("%s",ss);
		int pos=0;
		for(int i=0;i<(int)strlen(ss);i++){
			if(!nx[pos][ss[i]-'A']) nx[pos][ss[i]-'A']=++tot;
			pos=nx[pos][ss[i]-'A'];
		}
		sz[pos]|=1;
		return;
	}
	inline void build(){
		queue<int>q;
		for(int i=0;i<26;i++) if(nx[0][i]) q.push(nx[0][i]);
		while(!q.empty()){
			int pos=q.front();
			q.pop();
			for(int i=0;i<26;i++){
				if(nx[pos][i]){
					sz[nx[pos][i]]|=sz[nx[fail[pos]][i]];
					fail[nx[pos][i]]=nx[fail[pos]][i];
					q.push(nx[pos][i]);
				}
				else nx[pos][i]=nx[fail[pos]][i];
			}
		}
		return;
	}
	inline void DP(){
		int ans=0,res=1;		
		f[0][0]=1;
		for(int i=1;i<=m;i++)
			for(int j=0;j<=tot;j++)
				for(int k=0;k<26;k++)
					if(!sz[nx[j][k]]) f[i][nx[j][k]]=(f[i][nx[j][k]]+f[i-1][j])%P;
		for(int i=0;i<=tot;i++) ans=(ans+f[m][i])%P;		
		for(int i=1;i<=m;i++) res=res*26%P;
		printf("%d\n",(res-ans+P)%P);
		return;
	}
}AC;
int main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++) AC.insert();
	AC.build();
	AC.DP();
	return 0;
}

完結撒花❀