[BZOJ1030] 文本生成器 AC自动机+DP

我好菜啊。。。都是抄了std后再去理解的。。。
补集思想,先求出所有不可读的,在减去即可。
f[i,j]表示在AC自动机上走i步到达j点构成单词的方案数。首先要把所有是单词结尾的点标记出来,是不能走的,而且沿着fail树能走到单词末尾的点也是不能走的,这个在建AC自动机的时候顺便处理一下。然后可以对AC自动机做一个小处理,如果trie[i,c]=0(不存在)那么就把trie[fail[i],c]赋给它,最后dp的时候就不要管fail树了。
转移便是f[i, trie[j,c]]=f[i, trie[j, c]]+f[i-1, j]。
我一直不是很理解哪些没有出现过的字母是怎么搞出来的,后来发现全在0上。。。

代码:

const
  maxn=6000;
  maxm=105;
  md=10007;
var
  n,m,i,tot,ans:longint;
  trie:array[0..maxn,'A'..'Z']of longint;
  fail:array[0..maxn]of longint;
  s:ansistring;
  f:array[0..maxm,0..maxn] of longint;
  place:array[-1..26*maxn]of boolean;
procedure ins(s:ansistring);
var
  i,len,p:longint;
begin
  p:=0;
  len:=length(s);
  for i:=1 to len do
  begin
    if trie[p,s[i]]=0 then begin inc(tot); trie[p,s[i]]:=tot; end;
    p:=trie[p,s[i]];
  end;
  place[p]:=true;
end;
procedure getac;
var
  i,head,tail,x:longint;
  dl:array[0..26*maxn]of longint;
  ic:char;
begin
  head:=1;
  tail:=1;
  dl[1]:=0;
  while head<=tail do
  begin
    x:=dl[head];
    for ic:='A' to 'Z' do
      if trie[x,ic]>0 then
      begin
        inc(tail);
        dl[tail]:=trie[x,ic];
        if x<>0 then fail[trie[x,ic]]:=trie[fail[x],ic];
      end
      else if x<>0 then trie[x,ic]:=trie[fail[x],ic];
    place[x]:=place[x]or place[fail[x]];
    inc(head);
  end;
end;
procedure solve;
var
  i,j,all:longint;
  ic:char;
begin
  f[0,0]:=1;
  for i:=1 to m do
    for j:=0 to tot do
      if place[j]=false then
        for ic:='A' to 'Z' do
          f[i,trie[j,ic]]:=(f[i,trie[j,ic]]+f[i-1,j])mod md;
  ans:=0;
  for i:=0 to tot do
    if place[i]=false then ans:=(ans+f[m,i])mod md;
  all:=1;
  for i:=1 to m do all:=(all*26) mod md;
  writeln((all-ans+md)mod md);
end;

begin
  tot:=0;
  fillchar(place,sizeof(place),false);
  fillchar(trie,sizeof(trie),0);
  fail[0]:=-1;
  readln(n,m);
  for i:=1 to n do
  begin
    readln(s);
    ins(s);
  end;
  getac;
  solve;
end.

你可能感兴趣的:(dp,AC自动机)