现在的位置: 首页 > 综合 > 正文

[AC自动机][fail树][BZOJ 3172][TJOI2013]单词

2017年11月16日 ⁄ 综合 ⁄ 共 1322字 ⁄ 字号 评论关闭

Description

某人读论文,一篇论文是由许多单词组成。但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次。每个单词由小写字母组成,N<=200,单词总长度不超过10^6。

Analysis
建出AC自动机,然后对于一个点i和它的Fail[i]反向连边。这样构造出来的是一个树。
对于一个单词它出现的次数就是这个单词的结尾节点X的子树大小。证明是容易的。
不过这里的子树大小指的是每一个节点被访问过的次数和...
上代码:
var
  b:array[1..1000000,'a'..'z'] of longint;
  ch:char;
  i,edge,sum,n,k:longint;
  fa,s,next,d,fr:array[1..1000000] of longint;
  c:array[1..1000000] of char;
  word:array[1..200] of longint;
  e:array[1..1000000,1..2] of longint;
procedure build_trie;
var
  now,i:longint;
begin
  sum:=1;
  for i:=1 to n do begin
    now:=1;
    repeat
      read(ch);
      if b[now,ch]=0 then begin
        inc(sum);
        b[now,ch]:=sum;
        c[sum]:=ch;
        fa[sum]:=now;
        now:=sum;
      end
      else now:=b[now,ch];
      inc(s[now]);
    until eoln;
    word[i]:=now;
    readln;
  end;
end;
procedure add(x,y:longint);
begin
  inc(edge); e[edge,1]:=y; e[edge,2]:=fr[x]; fr[x]:=edge;
end;
function get_next(x:longint):longint;
begin
  if (b[x,c[k]]<>0) and (b[x,c[k]]<>k)  then exit(b[x,c[k]]);
  if x=1 then exit(1) else get_next:=get_next(next[x]);
end;
procedure build_ac;
var
  h,t,i:longint;
begin
  h:=0; t:=1; d[t]:=1; next[1]:=1;
  repeat
    inc(h);
    k:=d[h];
    if k<>1 then next[k]:=get_next(fa[k]);
    for ch:='a' to 'z' do
      if b[k,ch]<>0 then begin inc(t); d[t]:=b[k,ch]; end;
  until h=t;
  for i:=2 to sum do add(next[i],i);
end;
procedure dfs(x:longint);
var
  k:longint;
begin
  k:=fr[x];
  while k<>0 do begin
    dfs(e[k,1]);
    s[x]:=s[x]+s[e[k,1]];
    k:=e[k,2];
  end;
end;
begin
  readln(n);
  build_trie;
  build_ac;
  dfs(1);
  for i:=1 to n do writeln(s[word[i]]);
end.

抱歉!评论已关闭.