考察内容:AC自动机+DP
题意:给定一组字符串,问一个长为m的串中出现这些串的可能种数
分析:
首先正面求必会出现大量容斥现象,于是求它的补集,即为求不出现这些串的可能种数。
考虑建立一条路径,即为这个串的表示。图的路径即为通过next和fail数组链接,注意为0的next表示连向根节点,一开始从根节点出发,要求不经过end标记过的点(即字符串的尾节点)的路径,可以通过DP转移。
转移方程即为dp[i][j]+=dp[i-1][k],这里的i指的已走了i个结点,j为将要转移到的状态结点,k为转移前的结点。转移初始条件为dp[0][0]=1,意为初始没有走任何结点时从根结点出发的情况只有一种
题意:给定一组字符串,问一个长为m的串中出现这些串的可能种数
分析:
首先正面求必会出现大量容斥现象,于是求它的补集,即为求不出现这些串的可能种数。
考虑建立一条路径,即为这个串的表示。图的路径即为通过next和fail数组链接,注意为0的next表示连向根节点,一开始从根节点出发,要求不经过end标记过的点(即字符串的尾节点)的路径,可以通过DP转移。
转移方程即为dp[i][j]+=dp[i-1][k],这里的i指的已走了i个结点,j为将要转移到的状态结点,k为转移前的结点。转移初始条件为dp[0][0]=1,意为初始没有走任何结点时从根结点出发的情况只有一种
注意这里AC自动机代码中的小改动。若fail指向的结点有end标记,那么之前的串必然包含这个单词,所以需要将这个串上的指向end的结点也进行end标记.
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<queue>
using namespace std;
const int maxn=105;
const int mod=10007;
int cnt,n,m;
int next[maxn*maxn][26];
int fail[maxn*maxn];
char s[maxn];
int dp[maxn][maxn*maxn];
bool end[maxn*maxn];
struct acm
{
void insert()
{
scanf("%s",s+1);
int len=strlen(s+1);
int now=0;
for(int i=1;i<=len;i++)
{
if(!next[now][s[i]-'A'])next[now][s[i]-'A']=++cnt;
now=next[now][s[i]-'A'];
}
end[now]=true;
}
void getfail()
{
queue<int>q;
for(int i=0;i<26;i++)
if(next[0][i])q.push(next[0][i]);
while(!q.empty())
{
int t=q.front();
q.pop();
for(int i=0;i<26;i++)
{
if(!next[t][i])continue;
int k=fail[t];
while(!next[k][i]&&k)k=fail[k];
fail[next[t][i]]=next[k][i];
if(end[next[k][i]])end[next[t][i]]=true;
q.push(next[t][i]);
}
}
}
}acm;
int getpow(int x,int y)
{
int res=1;
while(y)
{
if(y&1)res=(res*x)%mod;
x=(x*x)%mod;
y>>=1;
}
return res;
}
void solve()
{
dp[0][0]=1;
for(int i=1;i<=m;i++)
for(int j=0;j<=cnt;j++)
if(dp[i-1][j]&&!end[j])
for(int k=0;k<26;k++)
{
int now=j;
while(!next[now][k]&&now)now=fail[now];
int tmp=next[now][k];//next=0表示此时写出的是一个根节点中也没有的一个单词路径
if(!end[tmp])dp[i][tmp]=(dp[i][tmp]+dp[i-1][j])%mod;
}
int ans=getpow(26,m);
for(int i=0;i<=cnt;i++)ans=(ans-dp[m][i]+mod)%mod;
printf("%d",ans);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)acm.insert();
acm.getfail();
solve();
return 0;
}