给定很多串,很容易想到会用
Trie
或之类的算法啦、
单纯的
Trie
好像不太可做,就想想
AC自动机
辣。(反正不是我想的)
然后直接计算不好算辣,就容斥一下求一个串都没出现的方案数辣。
然后每次转移的时候如果是被标记的结束节点就不管啦
for(int i=1;i<=m;i++)
{
for(int j=0;j<=cnt;j++)
{
if(!f[i-1][j]||tag[j]) continue;
for(int k=0;k<26;k++)
f[i][g[j][k]]=(f[i-1][j]+f[i][g[j][k]])%mod;
}
}
然后就
wa
了 半个下午!?心态微崩。
然后再仔细看看别人的代码。。还需要
tag[k]|=tag[Next[k]];
然后秒懂啊!如果一个串后缀是一个存在的单词,这个点也需要被标记掉。
然后继续
wa
…….成功把整个下午耗完….
TMd板子又错了。心态大崩。
【代码】
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#define N 6005
using namespace std;
typedef long long ll;
const int mod=10007;
ll read()
{
ll x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
int n,m,cnt,ans;
int g[N][26],Next[N],tag[N],f[105][N];
void Add(char *s)
{
int now=0;
int len=strlen(s);
for(int i=0;i<len;i++)
{
if(!g[now][s[i]-'A']) g[now][s[i]-'A']=++cnt;
now=g[now][s[i]-'A'];
}
tag[now]++;
}
void Input_Init()
{
n=read(),m=read();
for(int i=1;i<=n;i++)
{
char s[101];
scanf("%s",s);
Add(s);
}
}
void Construct_Automation()
{
queue<int>q;
q.push(0);
while(!q.empty())
{
int k=q.front();q.pop();
for(int i=0;i<26;i++)
{
int &v=g[k][i];
if(v)
{
q.push(v);
Next[v]=k==0?0:g[Next[k]][i];
}
else v=k==0?0:g[Next[k]][i];
tag[k]|=tag[Next[k]];
}
}
}
int qpow(int x,int y)
{
int rtn=1;
while(y)
{
if(y&1) rtn=rtn*x%mod;
x=x*x%mod;y>>=1;
}
return rtn;
}
void Solve()
{
f[0][0]=1;
for(int i=1;i<=m;i++)
{
for(int j=0;j<=cnt;j++)
{
if(!f[i-1][j]||tag[j]) continue;
for(int k=0;k<26;k++)
f[i][g[j][k]]=(f[i-1][j]+f[i][g[j][k]])%mod;
}
}
ans=qpow(26,m);
for(int i=0;i<=cnt;i++)
if(!tag[i]) ans=(ans-f[m][i]+mod)%mod;
printf("%d\n",ans);
}
int main()
{
Input_Init();
Construct_Automation();
Solve();
return 0;
}