bzoj1030: [JSOI2007]文本生成器
mdzz调了一中午。。
用了解的单词建AC自动机
原问题显然不太好搞啊。。
那么转换一下sum-不包含了解单词的文本数
问题变成在AC机上走m步不到达ed节点的方案数
变成DP: f[i][j]表示走到第i个节点,走了j步
宽搜转移一下。
坑点就是了解的单词有一个是另一个的字串就会减多
这样的话要把fail全部跳一次
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; const int mod=10007; int MOD(int x){return (x%mod+mod)%mod;} struct Trie { int w[30],fail; bool ed; void clean(){memset(w,0,sizeof(w));fail=0;ed=false;} }tr[11000];int trlen; char ss[110]; void maketree() { int now=0,len=strlen(ss+1); for(int i=1;i<=len;i++) { int x=ss[i]-'A'+1; if(tr[now].w[x]==0) tr[now].w[x]=++trlen, tr[trlen].clean(); now=tr[now].w[x]; } tr[now].ed=true; } int q[11000]; void bfs() { int head=1,tail=2;q[1]=0; while(head!=tail) { int now=q[head]; for(int x=1;x<=26;x++) { int son=tr[now].w[x]; if(son!=0) { if(now==0)tr[son].fail=0; else { int p=tr[now].fail; while(p!=0&&tr[p].w[x]==0)p=tr[p].fail; tr[son].fail=tr[p].w[x]; } q[tail]=son; tail++; } } head++; } } //-------------AC_machine-------------- struct node { int now,uh; }list[1100000]; bool v[11000][110]; int f[11000][110]; int main() { freopen("generator.in","r",stdin); freopen("generator.out","w",stdout); int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%s",ss+1),maketree(); bfs(); memset(f,0,sizeof(f));f[0][0]=1; memset(v,false,sizeof(v));v[0][0]=true; int head=1,tail=2,ans=0; list[1].now=0;list[1].uh=0; while(head!=tail) { int now=list[head].now,uh=list[head].uh;head++; if(uh==m){ans=MOD(ans+f[now][uh]);continue;} for(int x=1;x<=26;x++) { int p=now; while(p!=0&&tr[p].w[x]==0)p=tr[p].fail; bool bk=true; int pp=tr[p].w[x]; while(pp!=0) { if(tr[pp].ed==true){bk=false;break;} pp=tr[pp].fail; } if(bk==true) { if(tr[p].w[x]!=0)p=tr[p].w[x]; f[p][uh+1]=MOD(f[p][uh+1]+f[now][uh]); if(v[p][uh+1]==false) { v[p][uh+1]=true; list[tail].now=p;list[tail].uh=uh+1; tail++; } } } } int sum=1; for(int i=1;i<=m;i++)sum=MOD(sum*26); printf("%d\n",MOD(sum-ans)); return 0; }
pain and happy in the cruel world.