Sol
AC自动机+DP
考虑到可读文本的情况很复杂
于是用 所有情况-不包含可读文本的情况
先将使用者了解的单词建立AC自动机
vis表示当前节点或其fail指向的点是否是某个可读串的末尾,这样的节点不能经过
f[i][j]
表示统计完长度为i的字符串,此时位于AC自动机的节点j的方案数
如果可以走,累计答案
具体看代码吧
Code
// by spli
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<iostream>
#include<queue>
using namespace std;
const int p=10007;
const int N=65*100;
int n,m;
char s[120];
struct AC_automaton{
int fail;
int son[26];
bool vis;
}t[N];int tot;
queue<int>q;
int f[110][N];
void build(){
int len=strlen(s+1);
int ch;
int now=0;
for(int i=1;i<=len;++i){
ch=s[i]-'A';
if(!t[now].son[ch]) t[now].son[ch]=++tot;
now=t[now].son[ch];
}
t[now].vis=1;
}
void getfail(){
for(int i=0;i<26;++i)
if(t[0].son[i]) q.push(t[0].son[i]),t[t[0].son[i]].fail=0;
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=0;i<26;++i){
int &v=t[u].son[i];
if(v) t[v].fail=t[t[u].fail].son[i],t[v].vis|=t[t[v].fail].vis,q.push(v);
else v=t[t[u].fail].son[i];
}
}
}
void dp(){
f[0][0]=1;
for(int i=1;i<=m;++i)
for(int j=0;j<=tot;++j){
if(t[j].vis) continue;
for(int k=0;k<26;++k)
(f[i][t[j].son[k]]+=f[i-1][j])%=p;
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i){
scanf("%s",s+1);
build();
}
getfail();
dp();
int sum=1,ans=0;
for(int i=1;i<=m;++i) (sum*=26)%=p;
for(int i=0;i<=tot;++i)
if(!t[i].vis) (ans+=f[m][i])%=p;
printf("%d\n",((sum-ans)%p+p)%p);
return 0;
}
AC自动机+DP 解题技巧
6429

被折叠的 条评论
为什么被折叠?



