【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 }

 


 

posted @ 2018-02-06 23:43  noble_(noblex)  阅读(270)  评论(0编辑  收藏
/* */