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.