题意简述
你要求有多少个字符串,使得:
- 长度为m
- 包含至少一个给定的单词。会给定 个单词。
膜
思路框架
用总共的方案数减去一个单词都不包含的方案数。前面那个是 ,后面那个在 自动机上跑 求解。
具体思路
首先,“至少一个”->“总共减去一个都没有”,是一个经典套路。这个不多说。
然后讲讲如何 。设 表示,长度为 ,匹配到 自动机的第 个位置。在建立 然后若合法,那么就从 转移到 。最后的答案是所有 的和。
实现注意
- AC自动机别写挂了
- 多开点空间,别怂
代码
#include<bits/stdc++.h>
using namespace std;
namespace Flandre_Scarlet
{
#define N 14444
#define mod 10007
#define F(i,l,r) for(int i=l;i<=r;++i)
#define D(i,r,l) for(int i=r;i>=l;--i)
#define Fs(i,l,r,c) for(int i=l;i<=r;c)
#define Ds(i,r,l,c) for(int i=r;i>=l;c)
#define Tra(i,u) for(int i=G.Start(u),__v=G.To(i);~i;i=G.Next(i),__v=G.To(i))
#define MEM(x,a) memset(x,a,sizeof(x))
#define FK(x) MEM(x,0)
bool cxk[N];
class Ah_Chtholly_Automaton
{
public:
int tr[N][26];
int tot;
void Init()
{
FK(tr);
tot=0;
}
int Insert(char s[])
{
int pos=0;
for(int i=0;s[i];++i)
{
int c=s[i]-'A';
if (!tr[pos][c])
{
tr[pos][c]=++tot;
}
pos=tr[pos][c];
}
cxk[pos]=1;
return pos;
}
int fail[N];
queue<int> Q;
void BuildFail()
{
FK(fail);
while(!Q.empty()) Q.pop();
F(i,0,25)
{
if (tr[0][i]) Q.push(tr[0][i]);
}
while(!Q.empty())
{
int u=Q.front();Q.pop();
F(i,0,25)
{
if (tr[u][i])
{
fail[tr[u][i]]=tr[fail[u]][i];
cxk[tr[u][i]]|=cxk[fail[tr[u][i]]];
//是否合法的判断:如果fail不合法,那我也不合法
Q.push(tr[u][i]);
}
else tr[u][i]=tr[fail[u]][i];
}
}
}
}AC;
int n,m;
int id[N];
char tmp[N];
void Input()
{
scanf("%d%d",&n,&m);
AC.Init();
F(i,1,n)
{
scanf("%s",tmp);
id[i]=AC.Insert(tmp);
}
}
int dp[110][N];
int qpow(int a,int b,int m)
{
int r=1;
while(b)
{
if (b&1) r=r*a%m;
a=a*a%m,b>>=1;
}
return r;
}
void Soviet()
{
AC.BuildFail();
dp[0][0]=1;
F(i,0,m-1) F(j,0,AC.tot) F(k,0,25)
{
int ch=AC.tr[j][k];
if (!cxk[ch])
{
dp[i+1][ch]+=dp[i][j];
dp[i+1][ch]%=mod;
}
}
int ans=qpow(26,m,mod);
F(i,0,AC.tot)
{
ans=(ans-dp[m][i]+mod)%mod;
}
printf("%d\n",ans);
}
void IsMyWife()
{
Input();
Soviet();
}
}
int main()
{
Flandre_Scarlet::IsMyWife();
getchar();getchar();
return 0;
}