
An interesting special case of the keyword tree.
Typically not implemented with O(|S|^2) space or time.
Example word: banana$anana$nana$ana$na$a$





Failures: F[node(xW)]==node(W), hence `suffix links' Match down the edges of the tree When mismatches encountered, follow links
| ![]() |
Typical hacks


structures:
Stree { string=string, root=Node }
Node { start=int, length=int, slink=NIL, parent=NIL, children=[] }
remove-child(node,child-of-node): ...
add-child(node,new-child):...
fork-node(N,len):
# place a new node on edge between N and N's parent
X.start = N.start, X.length = len
# stitch it up
add-child(N.parent,X)
remove-child(N.parent,N)
add-child(X,N)
return X
get-child(T,N,char):
# return the child of N which extends N by character
for X in N.children:
if T.string[X.start+N.length]==char:
return X
return NIL
build-stree(S):
# a useful dollar at the end
T.string = S + '$'
# set up the root
T.root.start = 0
T.root.length = 0
T.root.parent = T.root
T.root.slink = T.root
# add suffixes in one by one
Len = length(T.string)
for 0<= i < Len:
# obtain the parent of the new leaf
par = find-or-make-parent(T,i)
# add the new leaf to the parent
add-child(par,node{start=i,length=Len-i})
return T
find-or-make-parent(T,i):
d = 0
N = root
while 1 :
#match down the tree along the edge into N
if d == N.length:
Ch = get-child(T,N,T.string[i+N.length])
if Ch == NIL:
return N
else
N = Ch
else # d < N.length
if T.string[N.start+d] == T.string[i+d]:
d = d+1
else
N = fork-node(N,d)
build-stree(S):
....
last = T.root
for 0<= i < Len:
par = find-or-make-parent(T,last,i)
add-child(par,node{start=i,length=Len-i})
last = par
return T
find-or-make-parent(T,N,i):
# guaranteed that we matched into N
# so free match on the suffix of N
d = max(0,N.length-1)
# if N has no slink, we fix it here
if N.slink=NIL:
L = N.parent.slink
# fast-match down the tree
while d != L.length:
if d > L.length:
L = get-child(T,L,T.string[i+L.length])
else
L = fork-node(L,d)
# we've either found or created the suffix link
# by the above
N.slink = L
N = N.slink
# now we do the rest of the search
while 1 :
# match down the tree along the edge into N
if d == N.length:
Ch = get-child(T,N,T.string[i+N.length])
....
Complexity variations:
| children | build-time | search-time | space |
| linked list | O(|A||S|) | O(|A||P|) | O(|S|) |
| arrays | O(|A||S|) | O(|P|) | O(|A||S|) |
| maps/trees | O(log|A| |S|) | O(log|A| |P|) | O(|S|) |
Implementation hacks
Known implementations