您的位置:首页 > Web前端 > JavaScript

AC自动机 【JSOI2007】bzoj1030 文本生成器

2016-12-27 19:43 411 查看
题目大意:

给出N个由大写字母组成的字符串,再给出一个长度M

求有多少个由大写字母组成的长度为M的字符串满足“至少包含这N个字符串中的一个”

答案对10007取模

题目分析:

直接求满足至少包含N个字符串中一个的字符串个数不好求,那我们可以求一个都不包含的个数,再用总个数(26^M)减去就可以了。

把所有的串扔到AC自动机中,我们避开单词节点做DP;

设f[i][j]代表到第i个字符,是第j个状态的不包含任何给出的字符串的方案数

f[i+1][k]+=f[i][j] (k是j的一个儿子且j与k都不是单词节点);

注意事项:

1、要注意一个字符串包含另一个字符串的情况;

2、DP用循环写,不要用递归(我原来用dfs写的,TLE,很糟糕,当然聪明的读者会有比我更好的方法)

3、f[0][0]赋值为1;

4、最后在模意义下做减法最好加上mod再取余mod。

代码如下:

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<cstring>
#define N 6060
using namespace std;
const int mod=10007;
int n,m,l,r;
char s[200];
int f[110]
;
struct trie{
trie *son[26],*fail;
bool dc;int pos;
trie()
{
memset(son,0,sizeof(son));
fail=NULL;dc=false;
}
void inser(char *s)
{
trie *c=this;
for(int i=0;s[i];i++)
{
if(!c->son[s[i]-'A']) c->son[s[i]-'A']=new trie();
c=c->son[s[i]-'A'];
}
c->dc=true;
}
}*V=new trie,*dl
;
void Aho_Corasick_Automaton(trie *V)
{
l=1;r=0;
V->fail=V;V->pos=0;
for(int i=0;i<26;i++)
{
if(!V->son[i]) V->son[i]=V;
else {dl[++r]=V->son[i];V->son[i]->fail=V;}
}
trie *c,*t;
while(l<=r)
{
c=dl[l++];
c->pos=l-1;
for(int i=0;i<26;i++)
{
if(!c->son[i]) c->son[i]=c->fail->son[i];
else
{
dl[++r]=c->son[i];
c->son[i]->fail=c->fail->son[i];
c->son[i]->dc|=c->fail->son[i]->dc;
}
}
}
return;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%s",s);
V->inser(s);
}
Aho_Corasick_Automaton(V);
f[0][0]=1;
dl[0]=V;
for(int i=0;i<m;i++)
for(int j=0;j<=r;j++)
if(!dl[j]->dc)
for(int k=0;k<26;k++)
f[i+1][dl[j]->son[k]->pos]=(f[i+1][dl[j]->son[k]->pos]+f[i][j])%mod;
int ans=1,a=26,t=m;
while(t)
{
if(t&1) ans=(ans*a)%mod;
a=(a*a)%mod;
t>>=1;
}
for(int i=0;i<=r;i++)
{
if(dl[i]->dc) continue;
ans=(ans-f[m][dl[i]->pos]+mod)%mod;
}
printf("%d\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  AC自动机 dp