传送门1
传送门2
写在前面:明天开始种树吗?
思路:感觉和Wireless Password有点像?f[i][j][k]表示到长度为i的字符串匹配到了自动机的节点j,k=1说明已经有可认识的单词,k=0表示没有。状态方程应该比较好想了吧,如果仍然不懂可以戳一下链接,这个题还不是状压呢。
(快速打完后样例过,但数组开小了导致RE,开大后交上去WA一半,发现建fail指针时对节点i没有处理num[i]和num[fail[i]]的关系,如果num[fail[i]]是1的话那么num[i]也应该是1,加了一步就A了……)
(Po姐的题解说可以用减法原理= =)
注意:好像要注意的已经在思路里说过了……
代码:
#include<bits/stdc++.h>
#define mod 10007
using namespace std;
int n,m,root=1,tot=1;
int trie[11000][26],fail[11000],f[103][10003][2];
char s[110];
bool num[11000];
queue<int>q;
void insert(char s[])
{
int len=strlen(s),now=root;
for (int i=0;i<len;i++)
{
if (!trie[now][s[i]-'A']) trie[now][s[i]-'A']=++tot;
now=trie[now][s[i]-'A'];
}
num[now]=1;
}
void build()
{
int now,tmp;
q.push(root);
while (!q.empty())
{
now=q.front();
q.pop();
num[now]|=num[fail[now]];
for (int i=0;i<26;i++)
if (trie[now][i])
{
tmp=fail[now];
while (tmp&&!trie[tmp][i]) tmp=fail[tmp];
if (tmp&&now!=root) fail[trie[now][i]]=trie[tmp][i];
else fail[trie[now][i]]=root;
q.push(trie[now][i]);
}
else
{
if (now==root) trie[now][i]=root;
else trie[now][i]=trie[fail[now]][i];
}
}
}
main()
{
scanf("%d%d",&n,&m);
int x,y,z,ans=0;
for (int i=1;i<=n;i++)
scanf("%s",s),
insert(s);
build();
f[0][1][0]=1;
for (int i=0;i<m;i++)
for (int j=1;j<=tot;j++)
for (int k=0;k<2;k++)
for (int l=0;l<26;l++)
x=i+1,y=trie[j][l],z=k|num[y],
f[x][y][z]=(f[x][y][z]+f[i][j][k])%mod;
for (int i=1;i<=tot;i++)
ans=(ans+f[m][i][1])%mod;
printf("%d",ans);
}