【BZOJ】1030: [JSOI2007]文本生成器(AC自动机+dp)
题目
传送门:QWQ
传送到洛谷QWQ
分析
我一开始也不会做这题的,后来看了很多网上的题解,终于AC了。(我好菜啊)
主要参考:传送门QWQ
直接搞非常麻烦,反正我是不会做。于是考虑求反,即求有多少不包含任何单词的数量。最后再用$ {26}^m $减去就ok了。
于是在$ AC $自动机上搞$ dp $。
用 $ dp[i][j] $表示前$ i $个字符在$ AC $自动机上位置为$ j $的方案数。
那么可以得出$ dp[i+1][k]=dp[i+1][k]+dp[i][j] $如果k是合法的儿子。
所以还要判断一下合法性(合法性 即:不是任何一个单词的end)
代码
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N=6010, MOD=10007; 4 5 int son[N][26], fail[N], end[N], newp, rt, q[N]; 6 int dp[1200][6010], n, m; 7 char s[N]; 8 9 int find(int cur,int i) 10 { 11 if(!cur) return rt; 12 if(son[cur][i]) return son[cur][i]; 13 return fail[son[cur][i]]=find(fail[cur],i); 14 } 15 16 int main() 17 { 18 for (int i=0;i<26;i++) son[0][i]=1; 19 newp=rt=1; 20 scanf("%d%d",&n,&m); 21 22 //Trie 23 for(int i=1;i<=n;i++) 24 { 25 scanf("%s",s+1); 26 int cur=rt, l=strlen(s+1); 27 for(int j=1;j<=l;j++) 28 { 29 if(!son[cur][s[j]-'A']) son[cur][s[j]-'A']=++newp; 30 cur=son[cur][s[j]-'A']; 31 } 32 end[cur]=1; 33 } 34 35 //fail 36 int l=1,r=1;q[1]=1; 37 for(;l<=r;l++) 38 { 39 for(int i=0;i<26;i++) 40 if(son[q[l]][i]) 41 { 42 fail[son[q[l]][i]]=find(fail[q[l]],i); 43 q[++r]=son[q[l]][i]; 44 } 45 } 46 47 //dp 48 dp[1][1]=1; 49 for(int i=1;i<=m;i++) 50 for(int j=1;j<=newp;j++) 51 { 52 for(int k=0;k<26;k++) 53 { 54 int cur=j, f=0; 55 while(cur) 56 { 57 if(end[son[cur][k]]) { f=1; break; } 58 cur=fail[cur]; 59 } 60 if(f) continue; //不能放 61 cur=j; 62 while(!son[cur][k]) 63 { 64 cur=fail[cur]; //从j向fail[j]跳直到有k儿子 65 } 66 cur=son[cur][k]; 67 dp[i+1][cur]=(dp[i+1][cur]+dp[i][j])%MOD; 68 } 69 } 70 71 int anss=1, ans=0; 72 for (int i=1;i<=m;i++) anss=(anss*26)%MOD; //转换 73 for (int i=1;i<=newp;i++) 74 { 75 ans=(ans+dp[m+1][i])%MOD; //最终答案是所有dp[m+1][x]的和 76 } 77 78 printf("%d\n",(anss-ans+MOD)%MOD); 79 return 0; 80 }