好处:可用n(+一个常数)的空间存下一个字符串的所有子串,便于计算有关子串的答案,时间复杂度为o(n+一个常数))
状态0:即所有状态的祖宗
(盗一张图)某个构造完的后缀自动机
t0即为状态0
last:上一个加入的字符最初所在的状态,即上一个cur
cur:最新一个加入的字符所在的状态
link:离状态0 。 son[i][j]:第i个节点连出去的第j条边所连的状态
len:第i个状态到最初状态0的最长距离。
每加入一个字符时,
先将其len改为len[last]+1(len[cur]不会改变)
然后分两种情况讨论:
1,节点p(p初始为last)没有一条值为x(新加入的字符的ASCALL[也许需要更改])的边
- 如果p=0,那么给p加一条值为x的边,将link[cur]改为0
- 否则给p加一条值为x的边,p变为link[p],再次判断分类讨论
2,节点p有一条值为x的边
将q变为son[p,x]
- 如果len[q]=len[p]+1,则将link[cur]赋为q
- 否则克隆q,新增节点clo,其son与link均与q相同,len[clo]=len[p]+1,cur与q的link赋为clo
- 克隆完后将p的link链(包括p)上的点中son[ss,x](ss为p的link链上的点)为q的赋为clo,son[ss,x]<>q时退出
至此,后缀自动机构造完毕
注:这只是构造步骤,算法原理也需要理解。
贴一段标
//后缀自动机模板
var
i,last,q,p,total,x,cur,clo:longint;
s:ansistring;
n:int64;
bz:boolean;
len,link:array[0..150000]of longint; //len——节点i到0的最短路,link——状态i的(来源?)
son:array[0..150000,1..52]of longint; //节点i的儿子
begin
assign(input,'sf_suffixautomaton_template.in');reset(input);
assign(output,'sf_suffixautomaton_template.out');rewrite(output);
readln(s);
readln(n);
last:=0;
for i:=1 to length(s) do
begin
if s[i]<='Z'then x:=ord(s[i])-64
else x:=ord(s[i])-70; //字符转ASCALL码
inc(total);
cur:=total;
p:=last;
len[cur]:=len[last]+1; //新建节点cur,因其位置在后缀自动机中存储的原串上,所以len[cur]
//永远为上一个cur(即last)的len+1
bz:=false;
while son[p,x]=0 do //p是否有值为x的边
begin
if (p=0)and(son[p,x]=0) then
begin
son[p,x]:=cur; //走到link链的底部,无法再走了,所以将连完边后将link[cur]赋为0
link[cur]:=p;
break;
end;
son[p,x]:=total; //没边,便给p向cur连上一条值为x的边
p:=link[p]; //走link链
if son[p,x]<>0 then bz:=true; //说明新的p节点有这条边了,退出,打标记是为了保证连完边
//退出后不会误判
end;
if (p=0)and(son[p,x]=0) then //出来还要多判一次,为什么可以自己证一下
begin
son[p,x]:=cur;
link[cur]:=p;
bz:=false;
end;
if bz=true then
begin
q:=son[p,x];
if len[q]=len[p]+1 then link[cur]:=q //如果已有儿子len值合法则更新link[cur]
else
begin
inc(total); //否则克隆一个节点,其link值与son均与已有儿子相同
clo:=total;
link[clo]:=link[q];
son[clo]:=son[q];
len[clo]:=len[p]+1; //但其len等于len[p]+1
link[q]:=clo; //将cur和q的link值改为clo
link[cur]:=clo;
while (son[p,x]=q)do //将p的link链上(包括p点)的,连出的x边儿子为q的点的儿子更改为clo
begin
son[p,x]:=clo;
if p=0 then break;
p:=link[p];
end;
end;
end;
last:=cur; //更新last
end;
close(input);
close(output);
end.