在AC自动机上做DP
dp[i][j]表示在字符串里(文章)中第i个位置在自动机中第j个状态的方案数
当sh[j].son[k]是一个单词的结尾时,则累计答案,此时i右边的字符都可以在A~Z中任意选择,不转移到下一个状态
若不是单词的结尾,则转移到下一个状态。
是否是单词的结尾需要注意,并不只是在加到字典树时最后一个位置,还有其他能以fail指针指到这里的状态
这是因为若当前的状态失配,则可以直接转移的一个单词的结尾,符合题目条件。
#include <bits/stdc++.h> #define ll long long #define mod 10007 using namespace std; ll n,m,dp[110][6100],w,ans; ll c[200]; char a[200]; struct node { ll son[27],t,fail; }sh[6100]; void build(char a[]) { ll sz,p; sz=strlen(a+1); p=1; for (int i=1;i<=sz;i++) { ll num; num=a[i]-'A'; if (sh[p].son[num]==0) { w++; sh[p].son[num]=w; } p=sh[p].son[num]; if (i==sz) sh[p].t=1; } } void build_fail()//广搜求fail { queue <ll> q; q.push(1); while (!q.empty()) { ll f; f=q.front(); q.pop(); if (f!=1) sh[f].t=sh[f].t|sh[sh[f].fail].t;//注意 for (int i=0;i<=25;i++) { if (sh[f].son[i]==0) { if (f==1) sh[f].son[i]=1; else sh[f].son[i]=sh[sh[f].fail].son[i]; } else { if (f==1) sh[sh[f].son[i]].fail=1; else sh[sh[f].son[i]].fail=sh[sh[f].fail].son[i]; q.push(sh[f].son[i]); } } } } int main() { scanf("%lld%lld",&n,&m); w=1; for (int i=1;i<=n;i++) { scanf("%s",a+1); build(a); } dp[0][1]=1; build_fail(); c[0]=1; for (int i=1;i<=m+10;i++) c[i]=(c[i-1]*26)%mod; for (int i=0;i<m;i++) { for (int j=1;j<=w;j++) { if (sh[j].t==1) continue; for (int k=0;k<=25;k++) { if (sh[sh[j].son[k]].t==1) { ans=(ans+dp[i][j]*c[m-i-1])%mod; continue;//注意 } dp[i+1][sh[j].son[k]]=(dp[i+1][sh[j].son[k]]+dp[i][j])%mod;//转移 } } } printf("%lld ",ans%mod); }