Effiziente Konstruktion eines Suffix

Werbung
Effiziente Konstruktion eines Suffix-Baumes (Ukkonen 1992)
Definition
Einen impliziten Suffix-Baum für ein Wort w erhält man, indem man den Suffix-Baum für w$
erzeugt und dann der Reihe nach
1. das Symbol $ aus allen Kantenmarkierungen entfernt
2. jede Kante entfernt, die eine -Markierung hat und
3. unter Zusammenfassen der Markierungen jeden Knoten ungleich der Wurzel mit Ausgangsgrad 1 entfernt.
Einen impliziten Suffix-Baum eines Präfixes w[1 .. i] von w erhält man durch Anwendung der
obigen Regeln auf den Suffix-Baum für w[1 .. i]$.
Bemerkung:
• Im impliziten Suffix-Baum gibt es weniger als |w$| Blätter.
• Jeder Suffix von w ist Präfix einer Zeichenkette, die durch einen Knoten im Baum repräsentiert wird.
Im folgenden bezeichne Ti den impliziten Suffix-Baum für w[1 .. i].
Die Idee des Algorithmus:
• T1 besteht aus einer Wurzel und einem Blatt, das mit 1 markiert ist. Die Kantenmarkierung ist w[1].
• Ukkonens Algorithmus arbeitet in Phasen. In Phase i, 1 < i ≤ n, wird aus dem impliziten
Suffix-Baum Ti−1 der Baum Ti für das Präfix w[1 .. i] konstruiert.
• Die Phase i ist in i Erweiterungsschritte unterteilt, die in der Reihenfolge von 1 bis i
durchgeführt werden.
• Im Erweiterungsschritt j der Phase i wird das Suffix w[j .. i] von w[1 .. i] in den Baum
Ti−1 eingefügt. Man sucht also das Ende eines Pfads von der Wurzel, der w[j .. i − 1]
repräsentiert (dieses Wort wurde in Phase i − 1 eingesetzt!) und erweitert dann um das
Symbol w[i].
• Aus Tn erzeugt man dann den Suffix-Baum für w$.
Das Prinzip des Ukkonens Algorithmus:
Konstruiere T1 .
for i = 2 to n do
/* n = |w| */
begin
/* Phase i */
for j = 1 to i do
begin
/* Erweiterungsschritt j in der Phase i */
/* Finde Weg von der Wurzel, der w[j..i-1] repräsentiert. */
/* Dann erweitere man, falls notwendig, den Baum so, */
/* dass auch w[j..i] repräsentiert wird. */
end
end
Regeln zur Suffix-Erweiterung
Nachdem man den w[j .. i − 1] repräsentierenden Weg von der Wurzel aus gefunden hat, muss
der Baum so erweitert werden, dass er auch w[j .. i] repräsentiert.
(Erweiterung j in der Phase i.)
Regel 1 Der Weg endet in einem Blatt. Dann erweitere man die Markierung der letzten Kante
des Weges um w[i].
Regel 2 Der Weg endet so, dass es keine Fortsetzung mit w[i] im Baum gibt, d.h.
(i) er endet in einem internen Knoten und keine Kantenmarkierung zu einem Nachfolgerknoten beginnt mit w[i] oder
(ii) er endet innerhalb“ einer Kantenmarkierung und der nachfolgende Buchstabe ist
”
ungleich w[i].
Man erzeugt in beiden Fällen ein neues Blatt mit Markierung j und fügt es
(i) mit einer durch w[i] markierten Kante als direkten Nachfolger ein oder
(ii) über einen neu erzeugten internen Knoten mit dem Blatt als direkten Nachfolger
in“ die Kante ein.
”
Regel 3 Der Weg endet so, dass es eine Fortsetzung mit w[i] im Baum gibt. Dann muss nichts
getan werden.
Satz
Die naive Implementation des Ukkonen-Verfahrens benötigt O(|w|3 ) Schritte.
Definition von Suffix-Links
Sei aβ eine Zeichenkette mit a ∈ Σ, β ∈ Σ∗ . Ist u ein interner Knoten ungleich der Wurzel im
impliziten Suffix-Baum mit ρ(u) = aβ, und existiert ein weiterer Knoten v mit ρ(v) = β, dann
wird ein Zeiger von u auf v als Suffix-Link von u bezeichnet. Man schreibt auch v = s(u).
Lemma
Wird ein neuer interner Knoten in der Erweiterung j einer Phase i in den Baum eingesetzt
und repräsentiert dieser Knoten aβ, a ∈ Σ, β ∈ Σ∗ , dann gibt es im Baum einen Knoten v mit
ρ(v) = β oder in der Erweiterung j + 1 derselben Phase wird ein neuer interner Knoten v 0 mit
ρ(v 0 ) = β eingesetzt.
Korollar
Jeder in Ukkonens Algorithmus neu erzeugte interne Knoten besitzt spätestens nach der folgenden Erweiterung einen Suffix-Link.
Korollar
Für jeden impliziten Suffix-Baum Ti gilt: Ist v ein interner Knoten ungleich der Wurzel mit
ρ(v) = aβ, a ∈ Σ, β ∈ Σ∗ , dann existiert ein Knoten s(v) im Baum mit ρ(s(v)) = β.
Ablauf der Phase i unter Verwendung der Suffix-Links:
Der erste Erweiterungsschritt setzt w[1 .. i] in den Baum Ti−1 ein. Da dies die momentan längste
repräsentierte Zeichenkette im Baum ist, endet die Suche im Blatt und die Erweiterung muss
mit Regel 1 vorgenommen werden.
Hat man einen globalen Zeiger auf das Blatt mit Markierung 1, das ja in Ti−1 das Wort
w[1 .. i − 1] repräsentiert, kann der erste Erweiterungsschritt in jeder Phase in konstanter
Zeit duchgeführt werden.
Dann lassen sich die Erweiterungsschritte j mit 2 ≤ j ≤ i wie folgt durchführen:
1. Finde den ersten Knoten v am Ende oder oberhalb von w[j − 1 .. i − 1], der entweder die
Wurzel oder aber ein interner Knoten ist, der einen Suffix-Link besitzt.
Sei w[j − 1 .. i − 1] = ρ(v)γ, γ ∈ Σ∗ .
2. Ist v die Wurzel, suche das Ende von w[j .. i − 1] im Baum. Ist v keine Wurzel, folge dem
Suffix-Link zum Knoten s(v) und suche von dort aus das Ende von γ.
3. Wende die der Situation entsprechende Erweiterungsregel an, so dass w[j .. i] im Baum
repräsentiert wird.
4. Wurde im vorangehenden Erweiterungsschritt j − 1 durch Regel 2 ein neuer interner
Knoten ũ erzeugt, so folgt, dass w[j .. i − 1] am Knoten s(ũ) endet. Erzeuge Suffix-Link
ũ → s(ũ).
Trick 1 - der skip/count - Trick
Ist man einem Suffix-Link gefolgt, so muss man von dort aus das Ende einer Zeichenkette γ
suchen. Dies benötigt bei direkter Implementation O(|γ|) Schritte. Da das Suffix w[j .. i − 1]
im Baum Ti−1 repräsentiert ist, kann man aber ausgehend vom Knoten s(v) eine Kante mit
Markierung γ 0 suchen, die mit dem gleichen Buchstaben wie γ beginnt. Ist |γ 0 | < |γ|, dann
müssen die restlichen Zeichenvon γ 0 nicht verglichen werden! Dieser Schritt wird iteriert, bis
man das Ende vom Suffix w[j .. i − 1] erreicht hat.
Lemma
Sei v → s(v) ein Suffix-Link, dem im Ukkonen Algorithmus gefolgt wird. Zu diesem Zeitpunkt
gilt Stufe(v) ≤ Stufe(s(v)) + 1
Satz
Mit dem skip/count-Trick benötigt jede Phase des Ukkonen-Algorithmus O(n) Zeiteinheiten,
wobei n = |w| ist.
Trick 2 - Abbruch bei Regel 3
Wenn immer man in einer Phase beim Erweiterungsschritt j Regel 3 anwendet, kann diese
Phase beendet werden.
Trick 3 - Implizites Ausführen von Regel 1
Ein einmal erzeugtes Blatt mit Markierung j wird nie verändert! Also muss in jeder SuffixErweiterung j einer Phase i die Regel 1 angewendet werden. Um die Anwendung nicht jedesmal
durchführen zu müssen, ersetzt man die Kantenmarkierung w[r .. i], kodiert durch (r, i), durch
(r, e), wobei e eine globale Größe ist, die das momentane Ende der Zeichenkette angibt und bei
jeder Phase um 1 erhöht wird. Damit muss Regel 1 nie mehr ausgeführt werden.
Jede Phase besteht also aus einer Anzahl von Erweiterungen nach Regel 1, gefolgt von Erweiterungen nach Regel 2, bei denen jeweils ein neues Blatt erzeugt wird, und dann Erweiterungen
nach Regel 3.
Trick 4 -Einschränken der Suffix-Erweiterungen
Da bei jeder Anwendung von Regel 2 ein Blatt erzeugt wird, merkt man sich also, bis zu
welchem Index ji−1 in der vorangegangenen Phase i − 1 Regeln 1 oder 2 angewendet wurden.
In der Phase i beginnt man dann mit den Suffix-Erweiterungen ab Schritt ji−1 + 1, bis man bei
j 0 erstmalig Regel 3 anwendet oder aber die Phase beendet ist.
Phase i endet mit der Kenntnis, wo w[j 0 .. i] in Ti repräsentiert wird. In der Phase i + 1 muss
also als erstes das Wort w[j 0 .. i + 1] eingesetzt werden, d.h. man befindet“ sich zu Beginn der
”
Phase i + 1 am richtigen“ Knoten, um eine Erweiterung durchzuführen. Damit kann die erste
”
Erweiterung in der Phase i + 1 in konstanter Zeit durchgeführt werden.
In zwei aufeinander folgenden Phasen gibt es also höchstens 2 explizite Erweiterungen, die den
gleichen Index haben.
Satz
Unter Verwendung von Suffix-Links und den Tricks 1 – 4 konstruiert Ukkonens Algorithmus
die impliziten Suffix-Bäume T1 bis Tn in O(n) Schritten.
Aus dem impliziten Suffix-Baum Tn für w lässt sich ein Suffix-Baum für w$ leicht erzeugen,
indem man mit der Zeichenkette w$ eine weitere Phase des Ukkonen-Algorithmus durchführt.
Satz
Ukkonens Algorithmus erzeugt einen Suffix-Baum für ein Wort w in O(|w|) Schritten.
Herunterladen