题目传送门
好题啊。
解法:
直接求很麻烦。
所以转化为总方案减去不合法的方案。
那么不合法的方案就相当于在字典树上面没有经过结尾节点的路径条数。
那么用f[i][j]表示走i步到了第j个节点的方案数。
对应的它下一步走到k。那么f[i+1][k]+=f[i][j]。
但是会出现一个问题。
就是。
比如说两个串。
abcd
cdef
那么当我们匹配了cd的时候其实应该可以继续匹配e的。
但是假设我们到了d,d是在abcd这条链上面的,我们就不能往下了因为下面没有了(在字典树上)
所以我们应该d连向e。。这样走到d时才能继续匹配e。
然后还有一点要注意的是:
因为我们是在结尾打标记。
然后如果他的fail指针指向的点有标记的话他自己也要打上标记因为你不能走到这里啊。
代码实现:
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cmath>
using namespace std;
struct node{int s,w[30],fail;node(){s=fail=0;memset(w,0,sizeof(w));}}tr[11000];int trlen;
int a[1100000],len;
void bt() {
int now=0;
for(int i=1;i<=len;i++) {
int x=a[i];
if(tr[now].w[x]==0)tr[now].w[x]=++trlen;
now=tr[now].w[x];
}tr[now].s=1;
}
int list[510000],head,tail;
void bfs() {
head=1,tail=2;list[1]=0;
while(head!=tail) {
int x=list[head];
for(int i=1;i<=26;i++) if(tr[x].w[i]==0)tr[x].w[i]=tr[tr[x].fail].w[i]; //如果我的fail有这个孩子就连边
else {
if(x!=0)tr[tr[x].w[i]].fail=tr[tr[x].fail].w[i];
if(tr[tr[tr[x].fail].w[i]].s==1)tr[tr[x].w[i]].s=1;
list[tail++]=tr[x].w[i];
}
head++;
}
}
char s[1100000];
int f[210][11000];
const int mod=10007;
int main() {
trlen=0;
int n,m;scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) {
scanf("%s",s+1);len=strlen(s+1);
for(int j=1;j<=len;j++)a[j]=s[j]-'A'+1;bt();
}
bfs();memset(f,0,sizeof(f));f[0][0]=1;
for(int i=0;i<m;i++)for(int j=0;j<=trlen;j++) {
if(tr[j].s==1)continue;
for(int x=1;x<=26;x++) {
int k=tr[j].w[x];if(tr[k].s==1)continue; //不能走到结尾
f[i+1][k]=(f[i+1][k]+f[i][j])%mod;
}
}
int ans=1;for(int i=1;i<=m;i++)ans*=26,ans%=mod;
for(int i=0;i<=trlen;i++) if(tr[i].s==0)ans=(ans-f[m][i]+mod)%mod;printf("%d\n",ans);
return 0;
}