JSOI 2007 文本生成器
题目见链接。
题解:
建立在 AC自动机 上的 树形DP。
先把单词建立 trie 树,并标记是否为一个单词的结尾(bool : ed), 预处理 fail 指针, ed |= fail.ed
然后求 可读,可以用 总的 减去 不可读的
设 f[i][j] 表示 到了第 i 位,第 i 位上的节点编号为 j 的方案数,则
f[i][son[j]] = (f[i-1][j] + f[i][son[j]] ) % mo 。
最后用 ans - f[m][i] ( i 节点不为单词末尾)
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=6005,M=105,mo=10007; 4 char s[N]; 5 int rt,tot,q[N],head,tail,ans1=1,ans2,f[M][N],n,m; 6 struct node{ 7 int fail,nxt[26]; 8 bool ed; 9 }e[N]; 10 inline void insert(char *s) 11 { 12 rt=0; 13 for (int i=0; s[i]; ++i) 14 { 15 if (!e[rt].nxt[s[i]-'A']) 16 e[rt].nxt[s[i]-'A']=++tot; 17 rt=e[rt].nxt[s[i]-'A']; 18 } 19 e[rt].ed=1; 20 } 21 inline void getfail() 22 { 23 rt=0; head=tail=0; 24 for (int i=0; i<26; ++i) 25 if (e[rt].nxt[i]) q[++tail]=e[rt].nxt[i]; 26 while (head<tail) 27 { 28 int x=q[++head]; 29 for (int i=0; i<26; ++i) 30 if (e[x].nxt[i]) 31 { 32 e[e[x].nxt[i]].fail=e[e[x].fail].nxt[i]; 33 q[++tail]=e[x].nxt[i]; 34 } 35 else e[x].nxt[i]=e[e[x].fail].nxt[i]; 36 e[x].ed|=e[e[x].fail].ed; 37 } 38 } 39 inline void doing() 40 { 41 f[0][0]=1; 42 for (int i=1; i<=m; ++i) 43 for (int j=0; j<=tot; ++j) 44 { 45 if (e[j].ed) continue; 46 for (int k=0; k<26; ++k) 47 f[i][e[j].nxt[k]]=(f[i][e[j].nxt[k]]+f[i-1][j])%mo; 48 } 49 for (int i=0; i<=tot; ++i) 50 if (!e[i].ed) ans2+=f[m][i]; 51 for (int i=1; i<=m; ++i) 52 ans1=(ans1*26)%mo; 53 printf("%d ",((ans1-ans2)%mo+mo)%mo); 54 } 55 int main() 56 { 57 scanf("%d%d",&n,&m); 58 for (int i=1; i<=n; ++i) 59 scanf("%s",s),insert(s); 60 getfail(); 61 doing(); 62 return 0; 63 }
fighting fighting fighting!!!