bzoj 1030 AC自动机+dp
代码:
//先把给的单词建AC自动机并且转移fail,然后d[i][j]表示构造的文章到第i位时处在字典树的第j个节点的不包含单词的数量,最后用总的数量26^m //-d[m][0~sz]即可。其中不能走单词结尾的节点以及他们的fail。这里其实要把每个节点都连向他的26个后继,但是不连也没关系可以看作 //那些没出现的节点都转移成了0节点。 #include<iostream> #include<cstdio> #include<cstring> #include<queue> using namespace std; const int MAXN=6009; const int MOD=10007; int node[MAXN][30],val[MAXN],f[MAXN],sz,d[109][MAXN]; char s[109]; int n,m; void init() { sz=0; memset(node[0],0,sizeof(node[0])); val[0]=f[0]=0; memset(d,0,sizeof(d)); } void insert(char *s) { int len=strlen(s),rt=0; for(int i=0;i<len;i++){ int id=s[i]-'A'; if(!node[rt][id]){ node[rt][id]=++sz; memset(node[sz],0,sizeof(node[sz])); val[sz]=0; } rt=node[rt][id]; } val[rt]=1; } void get_fail() { queue<int>q; for(int i=0;i<26;i++){ int u=node[0][i]; if(u) { q.push(u);f[u]=0; } } while(!q.empty()){ int rt=q.front();q.pop(); for(int i=0;i<26;i++){ int u=node[rt][i]; if(!u){ node[rt][i]=node[f[rt]][i]; continue; } q.push(u); f[u]=node[f[rt]][i]; } val[rt]|=val[f[rt]]; } } void get_d() { d[0][0]=1; for(int i=1;i<=m;i++){ for(int j=0;j<=sz;j++){ if(val[j]||d[i-1][j]==0) continue; for(int k=0;k<26;k++)if(!val[node[j][k]]) d[i][node[j][k]]=(d[i][node[j][k]]+d[i-1][j])%MOD; } } } int main() { init(); scanf("%d%d",&n,&m); for(int i=0;i<n;i++){ scanf("%s",s); insert(s); } get_fail(); get_d(); int ans1=0,ans2=1; for(int i=0;i<=sz;i++)if(!val[i]) ans1=(ans1+d[m][i])%MOD; for(int i=1;i<=m;i++) ans2=(ans2*26)%MOD; printf("%d\n",(ans2+MOD-ans1)%MOD); return 0; }