题目来源:http://www.lydsy.com/JudgeOnline/problem.php?id=1030
f[i][j],i表示走到第几步,j表示走到树上的哪个结点,j不能是单词结点或沿fail指针能找到单词的点。
对每一个f[i-1][j],处理每一个它所能到达的状态f[i][x],x表示j继续匹配a-z所能到达的结点。
最后统计f[m][j]之和,用总方案数减去之,即为答案。
有一个问题,如果文章里存在模式串里没有的字母怎么办?
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int mod=10007,N=6100;
int f[101][N];
int A[N][26],sum[N],fail[N],cnt;
bool danger[N];
queue<int> q;
char s[N];
int n,m,ans1,ans2=1;
void ins (char *s) {
int n=strlen(s),p=1,c;
for (int i=0;i<n;i++) {
c=s[i]-'A';
if (!A[p][c]) A[p][c]=++cnt;
p=A[p][c];
}
danger[p]=1;
}
void getfail () {
int p; fail[1]=0;
q.push(1);
while (!q.empty()) {
p=q.front(); q.pop();
for (int i=0;i<26;i++) {
if (!A[p][i]) continue;
int k=fail[p];
while (!A[k][i]) k=fail[k];
fail[A[p][i]]=A[k][i];
if (danger[A[k][i]]) danger[A[p][i]]=1;
q.push(A[p][i]);
}
}
}
void dp (int x) {
for (int i=1;i<=cnt;i++) {
if (danger[i]||!f[x-1][i]) continue;
for (int j=0;j<26;j++) {
int k=i;
while (!A[k][j]) k=fail[k];
f[x][A[k][j]]=(f[x][A[k][j]]+f[x-1][i])%mod;
}
}
}
int main () {
scanf("%d%d",&n,&m);
cnt=1;
for (int i=0;i<26;i++) A[0][i]=1;
for (int i=1;i<=n;i++) {
scanf("%s",s);
ins(s);
}
getfail();
f[0][1]=1;
for (int i=1;i<=m;i++) dp(i);
for (int i=1;i<=m;i++)
ans2=(ans2*26)%mod;
for (int i=1;i<=cnt;i++)
if (!danger[i]) ans1=(ans1+f[m][i])%mod;
printf("%d",(ans2-ans1+mod)%mod);
}
//实际上如果文章里有模式串中不存在的字母,那这一部分字母会被统计至编号为1的结点,最后统计时也会算上