bzoj1030 [JSOI2007]文本生成器——AC自動機+DP
阿新 • • 發佈:2018-07-17
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