題目來源: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的結點,最後統計時也會算上