BZOJ1030 [JSOI2007]文本生成器 AC自动机 动态规划
欢迎访问~原文出处——博客园-zhouzhendong
去博客园看该题解
题目传送门 - BZOJ1030
题意概括
给出n个模式串,问长度为m的串中有多少个至少含有这n个模式串中的任意一个。
注意,所有的串仅由A~Z 26个大写字母构成。
题解
AC自动机好题。
先构建一个AC自动机。
然后在AC自动机上面跑dp。
建议开滚动数组。
dp[i][j]表示长度为i,在AC自动机上面走到了j的方案数。
每次把所有走到模式串尾的全动统计到答案并清零。
代码
#include <cstring> #include <algorithm> #include <cstdlib> #include <cstdio> #include <cmath> using namespace std; const int N=60+5,L=100+5,Trie_Size=N*L; const int mod=10007; struct Trie{ int fail,e; int next[26]; void set(){ fail=e=0; memset(next,0,sizeof next); } }tree[Trie_Size]; int cnt; void AC_init(){ cnt=1; tree[0].set(),tree[1].set(); for (int i=0;i<26;i++) tree[0].next[i]=1; } void add(char ch[]){ int len=strlen(ch),rt=1,t; for (int i=0;i<len;i++){ t=ch[i]-'A'; if (!tree[rt].next[t]){ tree[++cnt].set(); tree[rt].next[t]=cnt; } rt=tree[rt].next[t]; } tree[rt].e=1; } void build_AC(){ int q[Trie_Size],head=0,tail=0,rt,son,k; q[++tail]=1,tree[0].fail=1; while (head<tail){ rt=q[++head]; for (int i=0;i<26;i++){ son=tree[rt].next[i]; if (!son){ tree[rt].next[i]=tree[tree[rt].fail].next[i]; continue; } k=tree[rt].fail; while (!tree[k].next[i]) k=tree[k].fail; tree[son].fail=tree[k].next[i]; tree[son].e|=tree[tree[k].next[i]].e; q[++tail]=son; } } } int n,m,dp[Trie_Size],pre[Trie_Size],Pow[L]; char str[L]; int main(){ scanf("%d%d",&n,&m); AC_init(); for (int i=1;i<=n;i++){ scanf("%s",&str); add(str); } build_AC(); Pow[0]=1; for (int i=1;i<=m;i++) Pow[i]=Pow[i-1]*26%mod; memset(dp,0,sizeof dp); memset(pre,0,sizeof pre); dp[1]=1; int ans=0; for (int i=1;i<=m;i++){ for (int j=1;j<=cnt;j++) pre[j]=dp[j]; memset(dp,0,sizeof dp); for (int j=1;j<=cnt;j++) if (pre[j]>0) for (int k=0;k<26;k++) dp[tree[j].next[k]]=(dp[tree[j].next[k]]+pre[j])%mod; for (int j=1;j<=cnt;j++) if (tree[j].e){ ans=(ans+dp[j]*Pow[m-i])%mod; dp[j]=0; } } printf("%d",ans); return 0; }