[JSOI2007]文字生成器
阿新 • • 發佈:2018-12-21
解題思路
題目要求求出包含至少一個串的方案
考慮用總方案\(26^M\)減去不包含的方案
將給定\(N\)個串建出AC自動機
定義危險結點為該節點 在Trie上代表的串 包含了給定串中某個
由fail樹的性質可知,這類結點出現且僅出現在某個結尾結點在fail樹上的子樹內
然後就可以dp了
定義\(dp[i][j]\)為放了前\(i\)個字元,匹配到了自動機上的j結點
增加一個字元就是走到\(j\)在自動機上的某個兒子,但是要避免走到危險結點(即不轉移下去)
統計答案就是\(\sum dp[M][j]\),即加入M個字元後不在危險結點的方案數(即使途中經過也已經在dp時去掉了)
#include<iostream> #include<cstdio> #include<queue> const int P=10007; struct AC_automaton{ struct node{ int son[26]; int fail; bool dg; }T[100000]; int tot; void insert(std::string &S){ int len=S.length(),now=0; for (int i=0;i<len;i++){ int &nxt=T[now].son[S[i]-'A']; if (!nxt) nxt=++tot; now=nxt; } T[now].dg=true; } void build(){ std::queue<int> Q; for (int i=0;i<26;i++) if (T[0].son[i]) Q.push(T[0].son[i]); while (!Q.empty()){ int now=Q.front(); Q.pop(); T[now].dg|=T[T[now].fail].dg; for (int i=0;i<26;i++){ if (T[now].son[i]) T[T[now].son[i]].fail=T[T[now].fail].son[i],Q.push(T[now].son[i]); else T[now].son[i]=T[T[now].fail].son[i]; } } } }AC; int n,M; std::string S; int dp[101][6001],Ans; inline void chkadd(int &a,int b){a+=b;if (a>=P) a-=P;} int fast_pow(int a,int b){ int ret=1; for (b<<=1;b>>=1;a=a*a%P) if (b&1) ret=ret*a%P; return ret; } int main(){ scanf("%d%d",&n,&M); for (int i=1;i<=n;i++) std::cin>>S,AC.insert(S); AC.build(); dp[0][0]=1; for (int i=1;i<=M;i++){ for (int j=0;j<=AC.tot;j++){ if (AC.T[j].dg) continue; for (int k=0;k<26;k++) chkadd(dp[i][AC.T[j].son[k]],dp[i-1][j]); } } for (int i=0;i<=AC.tot;i++){ if (AC.T[i].dg) continue; chkadd(Ans,dp[M][i]); } printf("%d\n",(fast_pow(26,M)-Ans+P)%P); }