http://www.lydsy.com/JudgeOnline/problem.php?id=1030
给出一些单词,问长度为\(m\)的文章有多少文章中出现过任意一个或多个单词.
分析
文章总数为\(26^m\),减去没有出现过任意单词的文章数量就是答案.
那么如何求"没有出现过任意单词的文章的数量"呢?
我们用所有单词建立一个AC自动机,那么问题就转化成了在AC自动机上跑\(m\)步(相当于边跑边枚举),不经过单词节点(相当于没有单词成功匹配).在AC自动机上dp即可.
\(dp[i][j]\)表示文章的第\(i\)个字母在自动机的\(j\)号节点上的方案数.那么最终答案就是\(\sum_{i=0}^{sz}dp[m][i]\).
注意\(i\)是从0开始的,因为当走到AC自动机上没有的节点时就会走到0节点.
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int maxn=6000+5,maxm=100+5,type=26,mod=10007; 5 int n,m,a=1,b; 6 int dp[maxm][maxn]; 7 char s[maxm]; 8 struct Aho_Corasick{ 9 int sz,ch[maxn][type],f[maxn],q[maxn]; 10 bool val[maxn]; 11 Aho_Corasick(){ memset(val,false,sizeof val); } 12 inline void ins(char *s){ 13 int u=0,m=strlen(s+1); 14 for(int i=1;i<=m;i++){ 15 int c=s[i]-'A'; 16 if(!ch[u][c]) u=ch[u][c]=++sz; 17 else u=ch[u][c]; 18 } 19 val[u]=true; 20 } 21 inline void get_fail(){ 22 int L=1,R=0; 23 for(int c=0;c<type;c++){ 24 int u=ch[0][c]; 25 if(u){ f[u]=0;q[++R]=u; } 26 } 27 while(L<=R){ 28 int u=q[L++]; 29 for(int c=0;c<type;c++){ 30 if(!ch[u][c]){ ch[u][c]=ch[f[u]][c]; continue; } 31 f[ch[u][c]]=ch[f[u]][c]; 32 if(val[ch[f[u]][c]]) val[ch[u][c]]=true; 33 q[++R]=ch[u][c]; 34 } 35 } 36 } 37 inline void DP(int x){ 38 for(int i=0;i<=sz;i++){ 39 if(val[i]||!dp[x-1][i]) continue; 40 for(int j=0;j<type;j++){ 41 if(!val[ch[i][j]]) dp[x][ch[i][j]]=(dp[x][ch[i][j]]+dp[x-1][i])%mod; 42 } 43 } 44 } 45 }ac; 46 int main(){ 47 scanf("%d%d",&n,&m); 48 for(int i=1;i<=n;i++){ 49 scanf("%s",s+1); 50 ac.ins(s); 51 } 52 ac.get_fail(); 53 dp[0][0]=1; 54 for(int i=1;i<=m;i++) ac.DP(i); 55 for(int i=1;i<=m;i++) a=(a*type)%mod; 56 for(int i=0;i<=ac.sz;i++) b=(b+dp[m][i])%mod; 57 printf("%d\n",(a-b+mod)%mod); 58 return 0; 59 }