AC自动机上跑DP
注意的细节是fail链上有标记 该节点也要标记(就好比 “ ********123******** ” 索然最后没有出现123 但是中间出现了123 这种情况不可取!)
/************************************************************** Problem: 1030 User: lxy8584099 Language: C++ Result: Accepted Time:132 ms Memory:6044 kb ****************************************************************/ /* 有点像 1009 TG考试 不过这个数据挺小 不用矩阵加速 我们求出不合法的个数 用总数减去就行了 一个串的时候我们用KMP转移 因为有多个串 所以建个AC自动机 转移 之后的就和1009一模一样了 f[i][j]表示 表示前i个字符 目前节点为j 的不符合方案数 转移: f[i][ c[j][k] ] += f[i-1][j] (c[j][k]不是某个认识单词的结尾) 自动机没有儿子自动接上fail 方便!!! 考虑到如果某节点fail链跳到 0 的路上也遇到标记的话 这个点也是不可取的! 处理方法:我们在自动机上用或运算。 */ #include<queue> #include<cstdio> #include<cstring> using namespace std; const int N=1e4+50; const int MOD=10007; int c[N][26],last[N],n,m,tot; int f[105][N],ans=0,sum=1; bool vis[N]; void Add(char *s) { int l=strlen(s),u=0; for(int i=0;i<l;i++) { int v=s[i]-'A'; if(!c[u][v]) c[u][v]=++tot; u=c[u][v]; } vis[u]=1; } void Init() { scanf("%d%d",&n,&m);char s[105]; for(int i=1;i<=n;i++) scanf("%s",s),Add(s); } void Ac_build() { queue<int> q; for(int i=0;i<26;i++) if(c[0][i]) q.push(c[0][i]); while(!q.empty()) { int u=q.front();q.pop(); for(int v=0;v<26;v++) { if(c[u][v]) { last[c[u][v]]=c[last[u]][v]; vis[c[u][v]]|=vis[c[last[u]][v]]; // fial链有标记 该节点不能到达! q.push(c[u][v]);continue; } c[u][v]=c[last[u]][v]; // 这叫啥。。忘了。 } } } void Solve() { f[0][0]=1; for(int i=0;i<m;i++) for(int u=0;u<=tot;u++) for(int v=0;v<26;v++) if(!vis[c[u][v]]) (f[i+1][c[u][v]]+=f[i][u])%=MOD; for(int i=0;i<=tot;i++) (ans+=f[m][i])%=MOD; for(int i=1;i<=m;i++) (sum*=26)%=MOD; printf("%d\n",(sum-ans+MOD)%MOD); } int main() { Init(); Ac_build(); Solve(); return 0; }