1. 程式人生 > >bzoj1030 [JSOI2007]文本生成器——AC自動機+DP

bzoj1030 [JSOI2007]文本生成器——AC自動機+DP

print .com oid 方案 const ons 生成 轉化 生成器

題目:https://www.lydsy.com/JudgeOnline/problem.php?id=1030

求至少有一個單詞的文本串不太好求,所以轉化成求所有情況減去沒有一個單詞的文本串;

沒有一個單詞的文本串可以用AC自動機+DP求,設 f[i][j] 表示文本串長度為 i ,當前 Trie 樹上節點為 j 的方案數;

則 f[i][j] 可以轉到仍然不包含單詞的它的兒子的方案數中,同時文本串長度+1;

所以需要在 getfail 時把單詞結尾的屬性也轉移一下,因為 fail 是單詞結尾的話,自己也一定有一個後綴是單詞結尾,也就是自己這裏也包含單詞了;

最後統計答案就是所有的 f[m][i] 的和,其中 i 可以是 Trie 樹上任意一個點。

代碼如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
int const mod=10007;
int n,m,f[105][6005],cnt,ans;
char s[105];
queue<int>q;
struct N{int son[30],fail,end;}t[6005];
void build()
{
    int l=strlen(s),nw=0;
    for(int i=0;i<l;i++)
    {
        
if(!t[nw].son[s[i]-A])t[nw].son[s[i]-A]=++cnt; nw=t[nw].son[s[i]-A]; } t[nw].end=1; } void getfail() { t[0].fail=0; for(int i=0;i<26;i++) if(t[0].son[i]) { t[t[0].son[i]].fail=0; q.push(t[0].son[i]); } while(q.size()) {
int x=q.front(); q.pop(); for(int i=0;i<26;i++) { if(t[x].son[i]) { t[t[x].son[i]].fail=t[t[x].fail].son[i]; // t[t[x].son[i]].end|=t[t[t[x].son[i]].fail].end; q.push(t[x].son[i]); } else t[x].son[i]=t[t[x].fail].son[i]; } if(t[t[x].fail].end)t[x].end=1; } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { cin>>s; build(); } getfail(); f[0][0]=1;//! for(int i=0;i<m;i++) for(int nw=0;nw<=cnt;nw++) { if(t[nw].end||!f[i][nw])continue;// for(int j=0;j<26;j++) { int x=t[nw].son[j]; (f[i+1][x]+=f[i][nw])%=mod; } } ans=1; for(int i=1;i<=m;i++) (ans*=26)%=mod; for(int i=0;i<=cnt;i++) if(t[i].end==0)ans=((ans-f[m][i])%mod+mod)%mod; //end=0! printf("%d",ans); return 0; }

bzoj1030 [JSOI2007]文本生成器——AC自動機+DP