16.01.2015 Aufrufe

4. Textalgorithmen Arten von String-Matching-Problemen ...

4. Textalgorithmen Arten von String-Matching-Problemen ...

4. Textalgorithmen Arten von String-Matching-Problemen ...

MEHR ANZEIGEN
WENIGER ANZEIGEN

Erfolgreiche ePaper selbst erstellen

Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong><br />

<strong>4.</strong> <strong>Textalgorithmen</strong><br />

Die Suche <strong>von</strong> einem Muster in einem Text wird auch als <strong>String</strong> <strong>Matching</strong><br />

oder Pattern <strong>Matching</strong> bezeichnet.<br />

Generell besteht die Aufgabe darin,<br />

• einen <strong>String</strong> (das Muster, Pattern) der Länge m<br />

• in einem Text der Länge n zu finden,<br />

wobei n > m gilt.<br />

Je nach Freiheitsgraden bei der Suche unterscheidet man verschiedene<br />

<strong>Arten</strong> <strong>von</strong> <strong>String</strong>-<strong>Matching</strong>-<strong>Problemen</strong>.<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong><br />

Welche Stellen in einem Text stimmen mit einem Muster bis auf d<br />

Fehler überein (Distance-Match-Anfrage) Beispiel: agrep<br />

Editierdistanz: Wie kann man am “günstigsten” einen <strong>String</strong> s in einen<br />

<strong>String</strong> t überführen Beispiel: diff<br />

Wo braucht man <strong>String</strong>-<strong>Matching</strong>-Verfahren Z.B.:<br />

• Volltextdatenbanken, Retrievalsysteme, Suchmaschinen<br />

• Bioinformatik<br />

☞ In diesem Abschnitt lernen wir effziente Algorithmen für exaktes<br />

<strong>String</strong>-<strong>Matching</strong> kennen.<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 158<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 160<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong><br />

<strong>Arten</strong> <strong>von</strong> <strong>String</strong>-<strong>Matching</strong>-<strong>Problemen</strong><br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong><br />

Bezeichnungen<br />

Exaktes <strong>String</strong>-<strong>Matching</strong>: Wo tritt ein <strong>String</strong> pat in einem Text text<br />

auf Beispiel: fgrep<br />

<strong>Matching</strong> <strong>von</strong> Wortmengen: Gegeben sei eine Menge S <strong>von</strong> <strong>String</strong>s.<br />

Wo tritt in einem Text ein <strong>String</strong> aus S auf Beispiel: agrep<br />

<strong>Matching</strong> regulärer Ausdrücke: Welche Stellen in einem Text passen<br />

auf einen regulären Ausdruck Beispiel: grep, egrep<br />

Approximatives <strong>String</strong>-<strong>Matching</strong>: Welche Stellen in einem Text passen<br />

am besten auf ein Muster (Best-Match-Anfrage)<br />

• Ein Alphabet Σ ist eine endliche Menge <strong>von</strong> Symbolen. |Σ| bezeichnet<br />

die Kardinalität <strong>von</strong> Σ.<br />

• Ein <strong>String</strong> (Zeichenkette, Wort) s über einem Alphabet Σ ist eine endliche<br />

Folge <strong>von</strong> Symbolen aus Σ. |s| bezeichnet die Länge <strong>von</strong> s.<br />

• ɛ bezeichnet den leeren <strong>String</strong>.<br />

• Wenn x und y <strong>String</strong>s sind, dann bezeichnet xy die Konkatenation<br />

<strong>von</strong> x und y.<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 159<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 161


<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong><br />

• s[i] bezeichnet das i-te Element eines <strong>String</strong>s s (1 ≤ i ≤ |s|).<br />

s[i . . . j] bezeichnet den <strong>String</strong> s[i]s[i + 1] . . . s[j]. Für i > j gelte<br />

s[i . . . j] = ɛ.<br />

• Für einen <strong>String</strong> s (mit m = |s|) bezeichnet s R die Umkehrung<br />

s[m]s[m − 1] · · · s[1] <strong>von</strong> s.<br />

• Für zwei <strong>String</strong> x und y gilt x = y genau dann, wenn |x| = |y| = m<br />

und x[i] = y[i] für alle 1 ≤ i ≤ m gilt.<br />

• Wenn ω = xyz ein <strong>String</strong> ist, dann ist x ein Präfix und z ein Suffix<br />

<strong>von</strong> ω.<br />

Gilt ω ≠ x (ω ≠ z), dann ist x (z) ein echter Präfix (echter Suffix) <strong>von</strong><br />

ω.<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong><br />

Exaktes <strong>String</strong>-<strong>Matching</strong><br />

Problem <strong>4.</strong>1. [Exaktes <strong>String</strong>-<strong>Matching</strong>]<br />

pat und text.<br />

(a) Man bestimme, ob pat ein Substring <strong>von</strong> text ist.<br />

Gegeben sind die <strong>String</strong>s<br />

(b) Man bestimme die Menge aller Positionen, an denen pat in text auftritt.<br />

Diese Menge wird mit MAT CH(pat, text) bezeichnet.<br />

• Im folgenden wird nur die Variante (a) <strong>von</strong> Problem <strong>4.</strong>1 betrachtet.<br />

• Algorithmen für die Variante (b) erhält man durch einfache Modifikationen<br />

der Algorithmen für (a).<br />

• Im folgenden sei m = |pat| und n = |text|.<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 162<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 164<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong><br />

• Ein <strong>String</strong> x (mit m = |x|) heißt Substring (Faktor) <strong>von</strong> y, wenn ein i<br />

existiert mit x = y[i . . . i + m − 1]. Andere Sprechweisen: x tritt in y<br />

an Position i auf bzw. Position i ist ein Match für x in y.<br />

• x (mit m = |x|) heißt Subsequenz <strong>von</strong> y wenn Positionen i 1 < i 2 · · · <<br />

i m existieren mit x = y[i 1 ]y[i 2 ] . . . y[i m ].<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: naiver Algorithmus<br />

Der naive Algorithmus<br />

Der naive Ansatz besteht darin, für jede Position <strong>von</strong> text (bzw. solange<br />

pat ab der aktuellen Position in text paßt) <strong>von</strong> neuem zu testen, ob pat<br />

an dieser Position auftritt.<br />

Das allgemeine Schema für solch einen naiven Algorithmus lautet:<br />

for i := 1 to n − m + 1 do<br />

man prüfe, ob pat = text[i . . . i + m − 1] gilt<br />

• Die Prüfung kann nun “<strong>von</strong> links nach rechts” oder “<strong>von</strong> rechts nach<br />

links” erfolgen.<br />

• Dies führt zu unterschiedlichen naiven Algorithmen und darauf aufbauend<br />

zu unterschiedlichen Ansätzen der Verbesserung.<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 163<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 165


<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: naiver Algorithmus<br />

• Zunächst wird die Variante “<strong>von</strong> links nach rechts” betrachtet.<br />

Algorithmus <strong>4.</strong>1. [Naives <strong>String</strong>-<strong>Matching</strong> <strong>von</strong> links nach rechts]<br />

i := 1<br />

while i ≤ n − m + 1 do<br />

j := 1<br />

while j ≤ m and pat[j] = text[i + j − 1] do j := j + 1 end<br />

if j = m + 1 then return true<br />

i := i + 1<br />

end<br />

return false<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: naiver Algorithmus<br />

• Trotz der im Durchschnitt linearen Laufzeit lohnt sich der Einsatz <strong>von</strong><br />

“besseren” <strong>String</strong>-<strong>Matching</strong>-Algorithmen, denn:<br />

– die nachfolgenden <strong>String</strong>-<strong>Matching</strong>-Algorithmen haben sich nicht<br />

nur in der Theorie, sondern auch in der Praxis als erheblich effizienter<br />

erwiesen, und<br />

– die Realität gehorcht nicht immer den Gesetzen der Wahrscheinlichkeitstheorie.<br />

Satz <strong>4.</strong>1. Der naive Algorithmus <strong>4.</strong>1 löst Problem <strong>4.</strong>1 in Zeit O(nm)<br />

und Platz O(m).<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 166<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 168<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: naiver Algorithmus<br />

Analyse f ür naives <strong>String</strong>-<strong>Matching</strong><br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Morris und Pratt<br />

<strong>String</strong>-<strong>Matching</strong> nach Morris und Pratt<br />

• Für pat = a m−1 b und text = a n benötigt Algorithmus <strong>4.</strong>1<br />

Zeichenvergleiche (Worst Case).<br />

(n − m + 1)m = nm − m 2 + m<br />

• Bei einem binären Alphabet und zufällig erzeugten pat und text (jedes<br />

Zeichen unabhängig und jedes Symbol mit Wahrscheinlichkeit<br />

1/2) ergibt sich für die durchschnittliche Anzahl an Zeichenvergleichen:<br />

(2 − 2 −m )n + O(1)<br />

Algorithmus <strong>4.</strong>1 ist naiv im folgenden Sinn: nach jedem Mismatch an<br />

Stelle j <strong>von</strong> pat wird vergessen, daß pat bis zur Position j − 1 auf text<br />

paßt.<br />

Kommt es an Stelle j <strong>von</strong> pat zu einem Mismatch, so gilt:<br />

pat[1 . . . j − 1] = text[i . . . i + j − 2]<br />

Dies kann wie folgt ausgenutzt werden: Angenommen, pat tritt in text<br />

an einer Position i + s mit i < i + s < i + j − 1 auf. Dann muß gelten:<br />

pat[1 . . . j − s − 1] = pat[1 + s . . . j − 1]<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 167<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 169


<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Morris und Pratt<br />

Veranschaulichung:<br />

pat: a b c a - - - - -<br />

pat: a b c a b c d - - - - -<br />

text: - - - - - a b c a b c a - - - - -<br />

↑ ↑ ↑<br />

i i + s i + j − 1<br />

s ist hier der Betrag, um den pat nach rechts verschoben wird.<br />

Nach einem Mismatch an Position j <strong>von</strong> pat kann nur dann an i + s ein<br />

Match vorliegen, wenn pat[1 . . . j − s − 1] ein Suffix <strong>von</strong> pat[1 . . . j − 1]<br />

ist.<br />

Mit k := j − s folgt: pat[1 . . . k − 1] ist Suffix <strong>von</strong> pat[1 . . . j − 1].<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Morris und Pratt<br />

• Man ermittle in einer Preprocessingphase zu jedem 1 ≤ j ≤ m das<br />

größte k, so daß pat[1 . . . k − 1] echter Suffix <strong>von</strong> pat[1 . . . j − 1] ist.<br />

Der entsprechende Betrag wird mit border[j] bezeichnet.<br />

border[j] :=<br />

bzw.<br />

border[j] :=<br />

max {k | pat[1 . . . k − 1] = pat[j − k + 1 . . . j − 1]}<br />

1≤k≤j−1<br />

max {k | pat[1 . . . k−1] ist echter Suffix <strong>von</strong> pat[1 . . . j−1]}<br />

1≤k≤j−1<br />

Weiterhin gelte border[1] = 0<br />

• Im Algorithmus schiebe man bei einem Mismatch an Position j des<br />

Pattern dieses um s = j − border[j] Stellen nach rechts.<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 170<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 172<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Morris und Pratt<br />

Veranschaulichung:<br />

Text<br />

Pattern<br />

s<br />

naechstmoeglicher Match<br />

Mismatch<br />

1 j<br />

Damit man keinen Match verpaßt, muß s möglichst klein und somit k<br />

möglichst groß gewählt werden.<br />

Konsequenzen für einen verbesserten Algorithmus:<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Morris und Pratt<br />

• Durch die Maximalität ist s ein “safe shift”.<br />

Algorithmus <strong>4.</strong>2. [Morris und Pratt]<br />

i := 1; j := 1<br />

while i ≤ n − m + 1 do<br />

while j ≤ m and pat[j] = text[i + j − 1] do<br />

j := j + 1<br />

end<br />

if j = m + 1 then return true<br />

i := i + j − border[j]<br />

j := max(border[j], 1)<br />

end<br />

return false<br />

(A)<br />

(B)<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 171<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 173


<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Morris und Pratt<br />

Lemma <strong>4.</strong>2. Wenn border[j] (für 1 ≤ j ≤ m) bereits vorliegt, löst Algorithmus<br />

<strong>4.</strong>2 Problem <strong>4.</strong>1 in Zeit O(n).<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Morris und Pratt<br />

Fibonacci-<strong>String</strong>s<br />

Beweis.<br />

• Es gibt höchstens n − m + 1 nicht erfolgreiche Vergleiche (nämlich<br />

höchstens einer für jedes i).<br />

• Man betrachte nun den Term i + j. Es gilt 2 ≤ i + j ≤ n + 1.<br />

• Immer wenn der Vergleich pat[j] = text[i+j −1] erfolgreich war, wird<br />

i + j um eins erhöht.<br />

Der n-te Fibonacci-<strong>String</strong> F n (n ≥ 0) ist wie folgt defi-<br />

Definition <strong>4.</strong>1.<br />

niert:<br />

• F 0 = ɛ<br />

• F 1 = b<br />

• F 2 = a<br />

• Insgesamt wird i + j in (A) und (B) nicht verringert.<br />

• F n = F n−1 F n−2 für n > 2<br />

⇒ Es gibt ≤ n erfolgreiche Vergleiche<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 174<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 176<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Morris und Pratt<br />

⇒ und somit O(n) Vergleiche insgesamt.<br />

✷<br />

Es bleibt das Problem, border[j] berechnen zu müssen. Zunächst ein<br />

Beispiel für border[j].<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Morris und Pratt<br />

Beispiel <strong>4.</strong>1. border[j] lautet für F 7 :<br />

j 1 2 3 4 5 6 7 8 9 10 11 12 13<br />

pat[j] a b a a b a b a a b a a b<br />

border[j] 0 1 1 2 2 3 4 3 4 5 6 7 5<br />

Für ababcaabaccba ergibt sich:<br />

j 1 2 3 4 5 6 7 8 9 10 11 12 13<br />

pat[j] a b a b c a a b a c c b a<br />

border[j] 0 1 1 2 3 1 2 2 3 4 1 1 1<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 175<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 177


<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Morris und Pratt<br />

Berechnung <strong>von</strong> border[j]<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Morris und Pratt<br />

Lemma <strong>4.</strong>3. Algorithmus <strong>4.</strong>3 benötigt zur Berechnung der Tabelle<br />

border höchstens O(m) Vergleiche.<br />

Beweis. Analog zu Lemma <strong>4.</strong>2. ✷<br />

• Zur Berechnung der Tabelle border wird eine spezielle Version <strong>von</strong><br />

Algorithmus <strong>4.</strong>2 benutzt.<br />

• Man nutzt dabei die aus dem Beweis <strong>von</strong> Lemma <strong>4.</strong>2 bekannte Tatsache<br />

aus, daß der Betrag i + j nie verringert wird.<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 178<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 180<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Morris und Pratt<br />

Algorithmus <strong>4.</strong>3.<br />

border[1] := 0<br />

for j := 2 to m do border[j] := 1 end<br />

i := 2<br />

j := 1<br />

while i ≤ m do<br />

while i + j − 1 ≤ m and pat[j] = pat[i + j − 1] do<br />

j := j + 1<br />

border[i + j − 1] := j<br />

end<br />

i := i + j − border[j]<br />

j := max(border[j], 1)<br />

end<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Knuth, Morris und Pratt<br />

Knuth, Morris und Pratt<br />

Der Algorithmus <strong>von</strong> Morris und Pratt kann noch effizienter gemacht<br />

werden. Man betrachte folgendes Beispiel:<br />

pat: a b a a b a - -<br />

text: - - - - - a b a a b c - -<br />

Es kommt für j = 6 zu einem Mismatch. Wegen border[6] = 3 wird pat<br />

um drei Zeichen nach rechts geschoben.<br />

pat: a b a a b a - -<br />

text: - - - - - a b a a b c - -<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 179<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 181


<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Knuth, Morris und Pratt<br />

Es kommt also an der gleichen Stelle des Textes direkt wieder zu einem<br />

Mismatch.<br />

Dies war abzusehen, denn für pat gilt: pat[6] = pat[border[6]]<br />

Veranschaulichung:<br />

Pattern<br />

s<br />

Mismatch<br />

1 j<br />

zwangslaeufiger Mismatch<br />

!= notwendig<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Knuth, Morris und Pratt<br />

Beispiel <strong>4.</strong>2. sborder[j] lautet für F 7 (vgl. Beispiel 1.1):<br />

j 1 2 3 4 5 6 7 8 9 10 11 12 13<br />

pat[j] a b a a b a b a a b a a b<br />

sborder[j] 0 1 0 2 1 0 4 0 2 1 0 7 1<br />

border[j] 0 1 1 2 2 3 4 3 4 5 6 7 5<br />

Algorithmus <strong>4.</strong><strong>4.</strong> [Knuth, Morris und Pratt] Der Algorithmus <strong>von</strong> Knuth,<br />

Morris und Pratt ist identisch zu Algorithmus 1.2 bis auf die Tatsache,<br />

daß statt border die strengere Variante sborder verwendet wird.<br />

Zur Berechnung <strong>von</strong> sborder sind ebenfalls nur marginale Änderungen<br />

gegenüber Algorithmus 1.3 notwendig.<br />

Text<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 182<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 184<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Knuth, Morris und Pratt<br />

Offensichtlich kann die bisher genutzte Bedingung, daß pat[1 . . . k − 1]<br />

Suffix <strong>von</strong> pat[1 . . . j − 1] ist, um die Bedingung<br />

verschärft werden.<br />

pat[k] ≠ pat[j]<br />

Konsequenzen für eine Verbesserung: man benutze statt border[j] die<br />

folgende Variante sborder[j]:<br />

sborder[j] :=<br />

max {k | pat[1 . . . k−1] = pat[j−k+1 . . . j−1] und pat[k] ≠ pat[j]}<br />

1≤k≤j−1<br />

Falls kein solches k existiert, gelte sborder[j] = 0<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Knuth, Morris und Pratt<br />

Algorithmus <strong>4.</strong>5. [Berechnung <strong>von</strong> sborder]<br />

for j := 1 to m do sborder[j] := 0 end<br />

i := 2<br />

j := 1<br />

while i ≤ m do<br />

while i + j − 1 ≤ m and pat[j] = pat[i + j − 1] do<br />

j := j + 1<br />

sborder[i + j − 1] := sborder[j]<br />

end<br />

sborder[i + j − 1] := max(j, sborder[i + j − 1])<br />

i := i + j − sborder[j]<br />

j := max(sborder[j], 1)<br />

end<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 183<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 185


<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Knuth, Morris und Pratt<br />

Satz <strong>4.</strong><strong>4.</strong> Die Algorithmen 1.3 und 1.4 lösen Problem <strong>4.</strong>1 in Zeit O(n +<br />

m) und Platz O(m).<br />

Beweis. sborder kann wie border in Zeit O(m) berechnet werden. Durch<br />

die Verwendung <strong>von</strong> sborder statt border finden in Algorithmus 1.4 nicht<br />

mehr Vergleiche statt als in Algorithmus 1.2. ✷<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Knuth, Morris und Pratt<br />

Nun erhält man i := 1 + 12 − sborder[12] = 6 und j := sborder[12] = 7.<br />

j<br />

↓<br />

a b a a b a b a a b a a b<br />

a b a a b a b a a b a c a b a a b a b a a b a a b<br />

↑<br />

i<br />

Es tritt wieder ein Mismatch auf, deshalb j := 4 und i := 9.<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 186<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 188<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Knuth, Morris und Pratt<br />

Beispiel <strong>4.</strong>3. [Algorithmus <strong>von</strong> Knuth, Morris und Pratt]<br />

F 7 wird in dem <strong>String</strong><br />

abaababaabacabaababaabaab<br />

Der <strong>String</strong><br />

gesucht. Nach dem Start des Algorithmus ergibt sich ein Mismatch für<br />

j = 12 und i = 1.<br />

j<br />

↓<br />

a b a a b a b a a b a a b<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Knuth, Morris und Pratt<br />

Die Mismatches setzen sich fort bis j = 1 und i = 13.<br />

j<br />

↓<br />

a b a a b a b a a b a a b<br />

a b a a b a b a a b a c a b a a b a b a a b a a b<br />

↑<br />

i<br />

Ab hier wird nun ein Match ermittelt.<br />

a b a a b a b a a b a c a b a a b a b a a b a a b<br />

↑<br />

i<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 187<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 189


<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Boyer und Moore<br />

Der Algorithmus <strong>von</strong> Boyer und Moore<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Boyer und Moore<br />

Veranschaulichung:<br />

naechstmoeglicher Match<br />

Der Algorithmus <strong>von</strong> Boyer und Moore kann als eine verbesserte Variante<br />

eines naiven <strong>String</strong>-<strong>Matching</strong>-Algorithmus angesehen werden, bei<br />

dem pat mit text <strong>von</strong> rechts nach links verglichen wird.<br />

Mismatch<br />

Vergleich<br />

Algorithmus <strong>4.</strong>6. [naives <strong>String</strong>-<strong>Matching</strong> <strong>von</strong> rechts nach links]<br />

i := 1<br />

while i ≤ n − m + 1 do<br />

j := m<br />

while j ≥ 1 and pat[j] = text[i + j − 1] do j := j − 1 end<br />

if j = 0 then return true<br />

i := i + 1<br />

end<br />

return false<br />

Text<br />

Pattern<br />

1<br />

<br />

j<br />

m<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 190<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 192<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Boyer und Moore<br />

Der Algorithmus <strong>von</strong> Boyer und Moore basiert auf der folgenden Überlegung:<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Boyer und Moore<br />

Kommt es in Algorithmus 1.6 an der Stelle j <strong>von</strong> pat zu einem Mismatch,<br />

so gilt<br />

• Tritt in Algorithmus 1.6 an Stelle j ein Mismatch auf und kommt<br />

pat[j + 1 . . . m] nicht ein weiteres mal in pat als Substring vor, so<br />

kann pat gleich um m Zeichen nach rechts verschoben werden.<br />

• Vergleicht man dagegen <strong>von</strong> links nach rechts, kann pat nach einem<br />

Mismatch an Position j nie um mehr als j Positionen nach rechts<br />

verschoben werden (vgl. Algorithmus 1.2).<br />

• pat[j + 1 . . . m] = text[i + j . . . i + m − 1] und<br />

• pat[j] ≠ text[i + j − 1].<br />

Dies kann wie folgt ausgenutzt werden: Angenommen, pat tritt in text an<br />

einer Position i < i + s < i + m auf. Dann müssen die beiden folgenden<br />

Bedingungen gelten:<br />

(BM1) für alle j < k ≤ m : k ≤ s oder pat[k − s] = pat[k]<br />

(BM2) s < j =⇒ pat[j − s] ≠ pat[j]<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 191<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 193


<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Boyer und Moore<br />

Veranschaulichung:<br />

pat:<br />

j − s<br />

↓<br />

- - - - a b c b c<br />

pat: - - - - a b c b c<br />

↑<br />

j<br />

text: - - - - - - a b c a b c - - - - -<br />

↑<br />

↑<br />

i i + j − 1<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Boyer und Moore<br />

Die entsprechenden Werte werden in einer Preprocessingphase ermittelt<br />

und in der Shift-Tabelle D abgelegt.<br />

D[j] := min{s| (BM1) und (BM2) gilt für j und s }<br />

s>0<br />

Der Algorithmus <strong>von</strong> Boyer und Moore verwendet nun im Falle eines<br />

Mismatches an Position j den in D[j] abgelegten Wert, um pat nach<br />

rechts zu verschieben.<br />

Damit man keinen Match verpaßt, muß s wiederum möglichst klein<br />

gewählt werden.<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 194<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 196<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Boyer und Moore<br />

Veranschaulichung:<br />

naechstmoeglicher Match<br />

fuer BM2 !=<br />

Vergleich<br />

= fuer BM1<br />

Mismatch<br />

Pattern<br />

1<br />

j<br />

m<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Boyer und Moore<br />

Algorithmus <strong>4.</strong>7. [Algorithmus <strong>von</strong> Boyer und Moore]<br />

i := 1<br />

while i ≤ n − m + 1 do<br />

j := m<br />

while j ≥ 1 and pat[j] = text[i + j − 1] do j := j − 1 end<br />

if j = 0 then return true<br />

i := i + D[j]<br />

end<br />

return false<br />

Text<br />

<br />

Beispiel <strong>4.</strong><strong>4.</strong> D[j] lautet für F 7 :<br />

j 1 2 3 4 5 6 7 8 9 10 11 12 13<br />

pat[j] a b a a b a b a a b a a b<br />

D[j] 8 8 8 8 8 8 8 3 11 11 6 13 1<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 195<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 197


<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Boyer und Moore<br />

Algorithmus 1.7 kann noch weiter verbessert werden: Tritt an Stelle m<br />

<strong>von</strong> pat (also bereits beim ersten Vergleich) ein Mismatch auf, so wird<br />

pat nur um eine Stelle nach rechts verschoben.<br />

Es sei<br />

last[c] := max {j | pat[j] = c}<br />

1≤j≤m<br />

und last[c] := 0 falls c nicht in pat auftritt.<br />

last[c] gibt für ein c ∈ Σ die jeweils letzte Position <strong>von</strong> c in pat an. Kommt<br />

es nun an Stelle j zu einem Mismatch, kann statt<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Boyer und Moore<br />

• Auf gewöhnlichen Texten verhält sich die vereinfachte Version i.d.R.<br />

nur marginal schlechter als die ursprüngliche Version.<br />

• Bei kleinem |Σ| ist die Occurence-Heuristik i.d.R. nutzlos.<br />

naechstmoeglicher Match fuer Occurence<br />

Mismatch<br />

Vergleich<br />

i := i + D[j]<br />

Pattern<br />

die Anweisung<br />

i := i + max(D[j], j − last[text[i + j − 1]])<br />

Text<br />

1<br />

<br />

j<br />

m<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 198<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 200<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Boyer und Moore<br />

verwendet werden. Damit ergeben sich noch größere Verschiebungen.<br />

Bemerkungen:<br />

• Verschiebungen der Länge j −last[text[i+j −1]] heißen Occurrence-<br />

Shift.<br />

• Wird nur der Occurence-Shift verwendet, d.h. die Verschiebeanweisung<br />

lautet<br />

i := i + max(1, j − last[text[i + j − 1]])<br />

so spricht man <strong>von</strong> einem vereinfachten Boyer-Moore-Algorithmus.<br />

• Die Worst-Case-Laufzeit des vereinfachten Boyer-Moore-Algorithmus<br />

beträgt O(nm).<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Boyer und Moore<br />

Es bleibt das Problem, die Shift-Tabelle D zu berechnen. Dies geschieht<br />

in zwei Phasen. In der ersten Phase werden nur Verschiebungen der<br />

Form D[j] < j betrachtet. Für solche Verschiebungen muß gelten:<br />

(BM1) pat[j + 1 . . . m] = pat[j + 1 − s . . . m − s]<br />

(BM2) pat[j − s] ≠ pat[j]<br />

Veranschaulichung:<br />

pat: * * * * b a b a a b a a b<br />

pat: * * * * b a b a a b a a b ← s →<br />

↑<br />

↑<br />

j − s j<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 199<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 201


<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Boyer und Moore<br />

Die Situation ist ähnlich wie beim Verfahren <strong>von</strong> Morris und Pratt. Statt<br />

eines Präfixes muß hier aber ein Suffix <strong>von</strong> pat mit einem inneren Teil<br />

<strong>von</strong> pat übereinstimmen.<br />

Vorgehensweise für die erste Phase: man berechnet (für 0 ≤ j ≤ m)<br />

rborder[j] mit<br />

rborder[j] :=<br />

bzw.<br />

max {k | pat[j + 1 . . . j + k − 1] = pat[m − k + 2 . . . m]}<br />

1≤k≤m−j<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Boyer und Moore<br />

i := m − 1<br />

j := 1<br />

while i >= 0 do<br />

while i − j + 1 >= 1 and pat[m − j + 1] = pat[i − j + 1] do<br />

j := j + 1<br />

rborder[i − j + 1] = j<br />

end<br />

i := i − j + rborder[m − j + 1]<br />

j := max(rborder[m − j + 1], 1)<br />

end<br />

rborder[j] :=<br />

max {k | pat[j+1 . . . j+k−1] ist echter Suffix <strong>von</strong> pat[j+1 . . . m]}<br />

1≤k≤m−j<br />

Weiterhin gelte rborder[m] = 0.<br />

Wegen (BM2) treten relevante Situationen zur Berechnung <strong>von</strong> D[j] nur<br />

im Falle eines Mismatch in der inneren While-Schleife auf. Dementsprechend<br />

wird zur Berechnung <strong>von</strong> D[j] der Algorithmus hinter der inneren<br />

While-Schleife um die folgenden Anweisungen erweitert:<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 202<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 204<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Boyer und Moore<br />

Beispiel <strong>4.</strong>5. rborder[j] lautet für F 7 :<br />

j 0 1 2 3 4 5 6 7 8 9 10 11 12 13<br />

pat[j] a b a a b a b a a b a a b<br />

rborder[j] 6 5 4 3 2 6 5 4 3 2 1 1 1 0<br />

Man beachte:<br />

• rborder[j] entspricht border[j] für pat R .<br />

• Dementsprechend läßt sich rborder[j] analog zu Algorithmus 1.3 berechnen,<br />

wobei man nun aber <strong>von</strong> rechts nach links vorgeht.<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Boyer und Moore<br />

if j > 1 then<br />

s := m − i<br />

t := i − j + 1<br />

if t + s > s then<br />

D[t + s] = min(s, D[t + s])<br />

endif<br />

endif<br />

Veranschaulichung:<br />

pat: * * * * b a b a a b a a b<br />

pat: * * * * b a b a a b a a b ← s →<br />

↑ ↑ ↑<br />

t = i − j + 1 t + s i<br />

Damit steht der Algorithmus für die erste Phase.<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 203<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 205


<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Boyer und Moore<br />

Beispiel <strong>4.</strong>6. D[j] nach der ersten Phase für den <strong>String</strong> F 7 :<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Boyer und Moore<br />

naechstmoeglicher Match<br />

j 1 2 3 4 5 6 7 8 9 10 11 12 13<br />

pat[j] a b a a b a b a a b a a b<br />

D[j] 13 13 13 13 13 13 13 3 13 13 6 13 1<br />

Mismatch<br />

Vergleich<br />

= fuer BM1<br />

Pattern<br />

1 t<br />

j<br />

m<br />

Text<br />

<br />

Es werden nun in absteigender Reihenfolge mögliche Werte für t betrachtet,<br />

und D[j] wird entsprechend korrigiert.<br />

Der größte mögliche Wert für t ergibt sich durch rborder[0] (Länge des<br />

längsten Substrings, der sowohl echter Präfix als auch echter Suffix ist).<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 206<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 208<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Boyer und Moore<br />

In der zweiten Phase werden Verschiebungen mit D[j] ≥ j betrachtet.<br />

Für solche Verschiebungen muß die Bedingung<br />

(BM1) pat[1 . . . t − 1] = pat[m − t + 2 . . . m] mit j ≤ m − t + 1<br />

gelten. (BM2) ist in dieser Phase stets wahr und braucht nicht weiter<br />

betrachtet zu werden.<br />

Veranschaulichung:<br />

pat: a b a a b a b a a b a a b<br />

pat: a b a a b a b a a b a a b ← s = m − t + 1 →<br />

↑ ↑ ↑<br />

j t − 1 m − t + 2<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Boyer und Moore<br />

Die weiteren möglichen Werte für t ergeben sich durch rborder[m−t+1].<br />

t := rborder[0]<br />

l := 1<br />

while t > 0 do<br />

s = m − t + 1<br />

for j := l to s do<br />

D[j] := min(D[j], s)<br />

end<br />

t := rborder[s]<br />

l := s + 1<br />

end<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 207<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 209


<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Boyer und Moore<br />

Algorithmus <strong>4.</strong>8. [Berechnung der Shift-Tabelle für Boyer und Moore]<br />

/* Initialisierung */<br />

rborder[m] := 0; D[m] := 1<br />

for j := m − 1 downto 0 do<br />

rborder[j] = 1; D[j] = m<br />

end<br />

/* Phase 1 */<br />

i := m − 1<br />

j := 1<br />

while i >= 0 do<br />

while i − j + 1 >= 1 and pat[m − j + 1] = pat[i − j + 1] do<br />

j := j + 1<br />

rborder[i − j + 1] = j<br />

end<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Boyer und Moore<br />

D[j] := min(D[j], s)<br />

end<br />

t := rborder[s]<br />

l := s + 1<br />

end<br />

Beispiel <strong>4.</strong>7. [Algorithmus <strong>von</strong> Boyer und Moore]<br />

in dem <strong>String</strong> abaababaabacabaababaabaab gesucht.<br />

a b a a b a b a a b a a b<br />

a b a a b a b a a b a a b<br />

a b a a b a b a a b a a b<br />

a b a a b a b a a b a a b<br />

a b a a b a b a a b a a b<br />

a b a a b a b a a b a a b<br />

a b a a b a b a a b a c a b a a b a b a a b a a b<br />

Der <strong>String</strong> F 7 wird<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 210<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 212<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Boyer und Moore<br />

if j > 1 then<br />

s := m − i<br />

t := i − j + 1<br />

if t + s > s then<br />

D[t + s] = min(s, D[t + s])<br />

endif<br />

endif<br />

i := i − j + rborder[m − j + 1]<br />

j := max(rborder[m − j + 1], 1)<br />

end<br />

/* Phase 2 */<br />

t := rborder[0]<br />

l := 1<br />

while t > 0 do<br />

s = m − t + 1<br />

for j := l to s do<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Boyer und Moore<br />

Beispiel <strong>4.</strong>8. [Shift-Tabellen für Boyer/Moore]<br />

datenbank retrieval compiler<br />

999999991 999999991 88888881<br />

kuckuck rokoko papa abrakadabra<br />

3336661 662641 2241 77777771131<br />

00<br />

Satz <strong>4.</strong>5. Algorithmus 1.7 löst Problem <strong>4.</strong>1(a) in Zeit O(n + m) und<br />

Platz O(m).<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 211<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 213


<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong> <strong>Matching</strong>: Boyer und Moore<br />

Bemerkungen:<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Approximatives <strong>String</strong>-<strong>Matching</strong><br />

Anwendungsbeispiele:<br />

• Als scharfe obere Schranke für die Anzahl an Zeichenvergleichen<br />

ergibt sich 3n.<br />

• Würde man statt (BM1) und (BM2) nur (BM1) verwenden, so wäre<br />

keine lineare Laufzeit mehr gewährleistet (O(mn)).<br />

• Sucht man mit dem Algorithmus <strong>von</strong> Boyer und Moore nach allen<br />

Matches für pat in text, so ist die Laufzeit ebenfalls O(mn).<br />

• Molekularbiologie (Erkennung <strong>von</strong> DNA-Sequenzen)<br />

• Ausgleich verschiedener Schreibweisen (Grafik vs. Graphik)<br />

• Ausgleich <strong>von</strong> Beugungen<br />

• Toleranz gegenüber Tippfehlern<br />

• Toleranz gegenüber OCR-Fehlern<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 214<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 216<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Approximatives <strong>String</strong>-<strong>Matching</strong><br />

Approximatives <strong>String</strong>-<strong>Matching</strong><br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Approximatives <strong>String</strong>-<strong>Matching</strong><br />

<strong>String</strong>-Metriken<br />

Der Begriff “nahezu” wird dabei durch eine Metrik auf <strong>String</strong>s formalisiert.<br />

• Bisher haben wir <strong>String</strong>-<strong>Matching</strong>-Probleme betrachtet, bei denen<br />

das Muster pat exakt mit einem Substring <strong>von</strong> text übereinstimmen<br />

mußte.<br />

• Beim <strong>Matching</strong> <strong>von</strong> regulären Ausdrücken läßt man zwar Varianten<br />

zu, aber ebenfalls keine Fehler.<br />

• In vielen praktischen Fällen ist es wünschenswert, die Stellen <strong>von</strong><br />

text zu finden, die mit pat “nahezu” übereinstimme, d.h. man erlaubt<br />

Abweichungen zwischen pat und text.<br />

Zur Erinnerung: Sei M eine Menge. Eine Funktion d : M × M −→ IR<br />

heißt Metrik, wenn die folgenden Bedingungen erfüllt sind:<br />

• d(x, y) ≥ 0 für alle x, y ∈ M<br />

• d(x, y) = 0 ⇔ x = y für alle x, y ∈ M<br />

• d(x, y) = d(y, x) für alle x, y ∈ M<br />

• d(x, z) ≤ d(x, y) + d(y, z) für alle x, y, z ∈ M.<br />

(M, d) ist dann ein metrischer Raum.<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 215<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 217


<strong>4.</strong> <strong>Textalgorithmen</strong> Approximatives <strong>String</strong>-<strong>Matching</strong><br />

Problem <strong>4.</strong>2. Gegeben seien ein <strong>String</strong> pat, ein <strong>String</strong> text, eine Metrik<br />

d für <strong>String</strong>s und ein ganze Zahl k ≥ 0.<br />

Man finde alle Substrings y <strong>von</strong> text mit d(pat, y) ≤ k.<br />

Bemerkungen:<br />

• Für k = 0 erhält man das exakte <strong>String</strong>-<strong>Matching</strong> Problem<br />

• Problem <strong>4.</strong>2 ist zunächst ein “abstraktes” Problem, da nichts über die<br />

Metrik d ausgesagt wird.<br />

• Zur Konkretisierung <strong>von</strong> Problem <strong>4.</strong>2 und zur Entwicklung <strong>von</strong> entsprechenden<br />

Algorithmen müssen zunächst sinnvolle Metriken betrachtet<br />

werden.<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Approximatives <strong>String</strong>-<strong>Matching</strong><br />

Editierdistanz, Levenstein-Metrik<br />

Definition <strong>4.</strong>3.<br />

• Für zwei <strong>String</strong>s x und y ist die Editierdistanz (Edit Distance)<br />

edit(x, y) definiert als die kleinste Anzahl an Einfüge- und Löschoperationen,<br />

die notwendig sind, um x in y zu überführen.<br />

• Läßt man zusätzlich auch die Ersetzung eines Symbols zu, so spricht<br />

man <strong>von</strong> einer Levenstein-Metrik (Levenshtein Distance) lev(x, y).<br />

• Nimmt man als weitere Operation die Transposition (Vertauschung<br />

zweier benachbarter Symbole) hinzu, so erhält man die Damerau-<br />

Levenstein-Metrik dlev(x, y).<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 218<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 220<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Approximatives <strong>String</strong>-<strong>Matching</strong><br />

Hamming-Distanz<br />

Definition <strong>4.</strong>2. Für zwei <strong>String</strong>s x und y mit |x| = |y| = m ergibt sich<br />

die Hamming-Distanz (Hamming Distance) durch:<br />

Bemerkungen:<br />

d(x, y) = |{1 ≤ i ≤ m|x[i] ≠ y[i]}|<br />

• Die Hamming-Distanz ist die Anzahl der Positionen, an denen sich x<br />

und y unterscheiden. Sie ist nur für <strong>String</strong>s gleicher Länge definiert.<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Approximatives <strong>String</strong>-<strong>Matching</strong><br />

Bemerkungen:<br />

• Offensichtlich gilt stets dlev(x, y) ≤ lev(x, y) ≤ edit(x, y).<br />

• Die Damerau-Levenstein-Metrik wurde speziell zur Tippfehlerkorrektur<br />

entworfen.<br />

• Wird in Problem <strong>4.</strong>2 für d eine der Metriken aus Definition 3.2 verwendet,<br />

dann spricht man auch <strong>von</strong> “string matching with k differences”<br />

bzw. <strong>von</strong> “string matching with k errors”.<br />

• Wird in Problem <strong>4.</strong>2 für d die Hamming-Distanz verwendet, so spricht<br />

man auch <strong>von</strong> “string matching with k mismatches”.<br />

Die Hamming-Distanz der <strong>String</strong>s abcabb und cbacba be-<br />

Beispiel <strong>4.</strong>9.<br />

trägt <strong>4.</strong><br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 219<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 221


<strong>4.</strong> <strong>Textalgorithmen</strong> Approximatives <strong>String</strong>-<strong>Matching</strong><br />

Beispiel <strong>4.</strong>10. Für x = abcabba und y = cbabac gilt:<br />

edit(x, y) = 5<br />

abcabba −→ bcabba −→ cabba −→ cbba −→ cbaba −→ cbabac<br />

dlev(x, y) = lev(x, y) = 4<br />

abcabba −→ cbcabba −→ cbabba −→ cbaba −→ cbabac<br />

abcabba −→ bcabba −→ cbabba −→ cbabab −→ cbabac<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Berechnung der <strong>String</strong>distanz<br />

• Da die Metriken edit, lev und dlev sehr ähnlich sind, wird im folgenden<br />

nur die Levenstein-Metrik betrachtet.<br />

• Algorithmen für die anderen Metriken erhält man durch einfache Modifikationen<br />

der folgenden Verfahren.<br />

• Im folgenden sei m = |x| und n = |y| und es gelte m ≤ n.<br />

☞ Lösungsansatz: dynamische Programmierung<br />

☞ genauer: berechne die Distanz der Teilstrings x[1 . . . i] und y[1 . . . j]<br />

auf der Basis bereits berechneter Distanzen.<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 222<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 224<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Berechnung der <strong>String</strong>distanz<br />

Berechnung der <strong>String</strong>distanz<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Berechnung der <strong>String</strong>distanz<br />

Die Tabelle LEV sei definiert durch:<br />

LEV [i, j] := lev(x[1 . . . i], y[1 . . . j]) mit 0 ≤ i ≤ m, 0 ≤ j ≤ n<br />

Problem <strong>4.</strong>3. Gegeben seien zwei <strong>String</strong>s x und y. Man ermittle<br />

edit(x, y) bzw. lev(x, y) bzw. dlev(x, y) sowie die zugehörigen Operationen<br />

zur Überführung der <strong>String</strong>s.<br />

Bemerkungen:<br />

• Wenn x und y Dateien repräsentieren, wobei x[i] bzw. y[j] die i-te<br />

Zeile bzw. j-te Zeile darstellt, dann spricht man auch vom File Difference<br />

Problem.<br />

• Unter UNIX steht das Kommando diff zur Lösung des File Difference<br />

Problems zur Verfügung.<br />

Die Werte für LEV [i, j] können mit Hilfe der folgenden Rekursionsformeln<br />

berechnet werden:<br />

• LEV [0, j] = j für 0 ≤ j ≤ n, LEV [i, 0] = i für 0 ≤ i ≤ m<br />

• LEV [i, j] =<br />

min{ LEV [i − 1, j] + 1,<br />

LEV [i, j − 1] + 1,<br />

LEV [i − 1, j − 1] + δ(x[i], y[j])}<br />

• δ(a, b) =<br />

{ 0 falls a = b<br />

1 sonst<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 223<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 225


<strong>4.</strong> <strong>Textalgorithmen</strong> Berechnung der <strong>String</strong>distanz<br />

Bemerkungen:<br />

• Die Rekursionsformel spiegelt die drei Operation Löschen, Einfügen<br />

und Substitution wider.<br />

• Die <strong>String</strong>distanz ergibt sich als LEV [m, n].<br />

• Möchte man nur die <strong>String</strong>distanz berechnen, so genügt es, sich auf<br />

Stufe i der Rekursion die Werte <strong>von</strong> LEV der Stufe i − 1 zu merken.<br />

• Benötigt man die zugehörigen Operationen, speichert man LEV<br />

als Matrix und ermittelt die zugehörigen Operationen in einer<br />

“Rückwärtsrechnung”.<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Berechnung der <strong>String</strong>distanz<br />

Beispiel <strong>4.</strong>11. Darstellung <strong>von</strong> LEV als Matrix für x = cbabac und y =<br />

abcabbbaa:<br />

a b c a b b b a a<br />

0 1 2 3 4 5 6 7 8 9<br />

c 1 1 2 2 3 4 5 6 7 8<br />

b 2 2 1 2 3 3 4 5 6 7<br />

a 3 2 2 2 2 3 4 5 5 6<br />

b 4 3 2 3 3 2 3 4 5 6<br />

a 5 4 3 3 3 3 3 4 4 5<br />

c 6 5 4 3 4 4 4 4 5 5<br />

Die zugehörigen Umwandlungen lauten:<br />

cbabac −→ ababac −→ abcabac −→ abcabbac −→ abcabbbac −→<br />

abcabbbaa<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 226<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 228<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Berechnung der <strong>String</strong>distanz<br />

Algorithmus <strong>4.</strong>9. [Berechnung der <strong>String</strong>distanz]<br />

for i := 0 to m do LEV [i, 0] := i end<br />

for j := 1 to n do LEV [0, j] := j end<br />

for i := 1 to m do<br />

for j := 1 to n do<br />

LEV [i, j] := min{ LEV [i − 1, j] + 1, LEV [i, j − 1] + 1,<br />

LEV [i − 1, j − 1] + δ(x[i], y[j])}<br />

end<br />

end<br />

return LEV [m, n]<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Berechnung der <strong>String</strong>distanz<br />

Veranschaulichung: Die Berechnung der <strong>String</strong>distanz kann als Pfad<br />

in einem Graphen veranschaulicht werden.<br />

c<br />

b<br />

a<br />

b<br />

a<br />

a b c a b b b a a<br />

=<br />

=<br />

=<br />

=<br />

=<br />

Löschoperation<br />

Einfügeoperation<br />

Substitution<br />

keine Änderung<br />

c<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 227<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 229


<strong>4.</strong> <strong>Textalgorithmen</strong> Berechnung der <strong>String</strong>distanz<br />

Der dargestellte Pfad entspricht der folgenden (nicht optimalen) Umwandlung:<br />

cbabac −→ acbabac −→ abcbabac −→ abcabac −→ abcabbac −→<br />

abcabbbac −→ abcabbbaa<br />

Aus der Rekursionsformel und den Bemerkungen folgt:<br />

Satz <strong>4.</strong>6. Die <strong>String</strong>distanz (für edit, lev und dlev) kann in Zeit O(mn)<br />

und Platz O(m) berechnet werden. Problem <strong>4.</strong>3 kann mit Platz O(mn)<br />

gelöst werden.<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Berechnung der <strong>String</strong>distanz<br />

Ansonsten berechnet sich MLEV [i, j] wie LEV [i, j], d.h.:<br />

• MLEV [i, 0] = i für 0 ≤ i ≤ m<br />

• MLEV [i, j] =<br />

min{ MLEV [i − 1, j] + 1,<br />

MLEV [i, j − 1] + 1,<br />

MLEV [i − 1, j − 1] + δ(x[i], y[j])}<br />

Gilt nun MLEV [m, j] ≤ k, so endet in Position j ein Substring y <strong>von</strong><br />

text mit lev(pat, y) ≤ k (wobei m die Patternlänge ist).<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 230<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 232<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Berechnung der <strong>String</strong>distanz<br />

Mit einer kleinen Änderung kann die angegebene Rekursionsformel<br />

auch zur Lösung <strong>von</strong> Problem <strong>4.</strong>2 eingesetzt werden.<br />

Es sei MLEV definiert durch:<br />

MLEV [i, j] := min {lev(pat[1 . . . i], text[l . . . j])}<br />

1≤l≤j<br />

d.h., MLEV [i, j] ist die kleinste Distanz zwischen pat[1 . . . i] und einem<br />

Suffix <strong>von</strong> text[1, j].<br />

Es gilt nun: MLEV [0, j] = 0 für 0 ≤ j ≤ n,<br />

denn pat[1 . . . 0] = ɛ und ɛ ist stets in text[1 . . . j] ohne Fehler enthalten.<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Berechnung der <strong>String</strong>distanz<br />

Beispiel <strong>4.</strong>12. Die Tabelle MLEV für pat = ABCDE und text =<br />

ACEABP CQDEABCR.<br />

j 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14<br />

i A C E A B P C Q D E A B C R<br />

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0<br />

1 A 1 0 1 1 0 1 1 1 1 1 1 0 1 1 1<br />

2 B 2 1 1 2 1 0 1 2 2 2 2 1 0 1 2<br />

3 C 3 2 1 2 2 1 1 1 2 3 3 2 1 0 1<br />

4 D 4 3 2 2 3 2 2 2 2 2 3 3 2 1 1<br />

5 E 5 4 3 2 3 3 3 3 3 3 2 3 3 2 2<br />

Für k = 2 ergeben sich die Positionen 3, 10, 13 und 1<strong>4.</strong> Die zugehörigen<br />

Substrings <strong>von</strong> text sind ACE, ABPCQDE, ABC und ABCR.<br />

Satz <strong>4.</strong>7. Problem <strong>4.</strong>2 kann für die Metriken edit, lev und dlev in Zeit<br />

O(mn) gelöst werden.<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 231<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 233


<strong>4.</strong> <strong>Textalgorithmen</strong> Trie<br />

Suffix-Bäume<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Trie<br />

Trie<br />

• In diesem Abschnitt sollen Datenstrukturen zur Beschleunigung des<br />

exakten <strong>String</strong>-<strong>Matching</strong>s (Problem <strong>4.</strong>1) untersucht werden.<br />

• Konkret liegt folgende Situation vor:<br />

– Gegeben ist ein (evtl. sehr langer) <strong>String</strong> text.<br />

– Man hat Anfragen an text gemäß Problem <strong>4.</strong>1, d.h. es sind alle<br />

(oder ein) Vorkommen eines beliebigen <strong>String</strong>s pat in text aufzufinden.<br />

– Gesucht ist eine Datenstruktur D, mit der nach einer Preprocessingphase<br />

für text solche Anfragen in sublinearer Zeit beantwortet<br />

werden können.<br />

Definition <strong>4.</strong><strong>4.</strong> Es sei Σ ein endliches Alphabet. Ein Trie (auch Σ-<br />

Baum genannt) ist ein Wurzelbaum mit den folgenden Eigenschaften:<br />

(i) Die Kanten des Baumes sind mit Zeichen aus Σ markiert.<br />

(ii) Für jeden Knoten k des Baums und jedes Zeichen c ∈ Σ gibt es<br />

höchstens eine Kante, die <strong>von</strong> k ausgeht und mit c markiert ist.<br />

Für einen Knoten k in einem Trie bezeichnet path(k) den <strong>String</strong>, der sich<br />

aus der Folge der Kantenmarkierungen auf dem Pfad <strong>von</strong> der Wurzel<br />

nach k ergibt.<br />

Für eine Menge P <strong>von</strong> <strong>String</strong>s bezeichnet T rie(P ) den kleinsten Trie<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 234<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 236<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Trie<br />

Anforderungen an die Datenstruktur D:<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Trie<br />

mit der Eigenschaft:<br />

• Die Größe <strong>von</strong> D sollte linear in |text| sein.<br />

∀pat ∈ P ∃k : pat = path(k)<br />

• D sollte effizient (möglichst in O(|text|)) aufgebaut werden können.<br />

• Problem <strong>4.</strong>1 (exaktes <strong>String</strong>-<strong>Matching</strong>) sollte mit Hilfe <strong>von</strong> D in sublinearer<br />

Zeit gelöst werden können.<br />

☞ Grundidee: Man finde eine geeignete Datenstruktur zur Repräsentation<br />

<strong>von</strong> Wortmengen.<br />

Beispiel <strong>4.</strong>13. [Trie] Für die<br />

Menge P = {ab, ba, babb, bb} <strong>von</strong><br />

<strong>String</strong>s ergibt sich der Trie:<br />

b<br />

a<br />

b<br />

a<br />

b<br />

b<br />

b<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 235<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 237


<strong>4.</strong> <strong>Textalgorithmen</strong> Trie<br />

<strong>String</strong>suche mit Hilfe eines Tries<br />

Es sei P eine Menge <strong>von</strong> <strong>String</strong>s. Dann gilt:<br />

• T rie(P ) kann in linearer Zeit bezüglich der Gesamtlänge der in P<br />

enthaltenen <strong>String</strong>s aufgebaut werden.<br />

• Die Größe <strong>von</strong> T rie(P ) ist linear in der Gesamtlänge der <strong>String</strong>s <strong>von</strong><br />

P .<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Trie<br />

• Ein erster Lösungsansatz besteht darin, sämtliche Suffixe eines Textes<br />

mit Hilfe eines Tries darzustellen.<br />

• Allgemein kann ein Suffix <strong>von</strong> text Präfix eines anderen Suffixes <strong>von</strong><br />

text sein. Dies würde dazu führen, daß nicht jeder Suffix mit einem<br />

Blatt des Tries assoziiert werden kann.<br />

• Deshalb wird die zusätzliche Voraussetzung eingeführt, daß text mit<br />

einem Sonderzeichen $ abgeschlossen sein muß, das ansonsten in<br />

text nicht vorkommt.<br />

• Es sei w ein beliebiger <strong>String</strong>. Dann kann mit Hilfe <strong>von</strong> T rie(P ) in<br />

O(|w|) geprüft werden, ob w ∈ P gilt.<br />

• Für jeden Trie gilt: Ausgangsgrad ≤ |Σ|.<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 238<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 240<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Trie<br />

Beispiel <strong>4.</strong>1<strong>4.</strong> Suche des<br />

<strong>String</strong>s ba in dem Trie für die<br />

Menge P = {ab, ba, babb, bb}.<br />

a<br />

b<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Trie<br />

Suffix-Trie<br />

• Man folgt ausgehend <strong>von</strong> der<br />

Wurzel sukzessive den Kanten<br />

des Tries, die mit dem Symbol<br />

pat[j] markiert sind.<br />

b<br />

b<br />

a<br />

b<br />

Definition <strong>4.</strong>5. Es sei s = s[1] . . . s[n − 1]$ ein <strong>String</strong>. Dann ist der<br />

Suffix-Trie ST (s) der Baum<br />

ST (s) = T rie({s[1 . . . n], s[2 . . . n], . . . , s[n − 1 . . . n], $})<br />

• Ist das Pattern erschöpft und<br />

der aktuelle Knoten markiert<br />

einen <strong>String</strong> aus P , so ist das<br />

Suchpattern in P .<br />

b<br />

wobei die Blätter p <strong>von</strong> ST (s) zusätzlich eine Markierung label(p) tragen,<br />

für die gilt:<br />

label(p) = i ⇐⇒ path(p) = s[i . . . n]<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 239<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 241


<strong>4.</strong> <strong>Textalgorithmen</strong> Trie<br />

a<br />

$<br />

b<br />

c<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Positionsbäume<br />

Positionsidentifikator<br />

7<br />

Beispiel <strong>4.</strong>15. [Suffix-Trie] Der<br />

Suffix-Trie für den <strong>String</strong> abcabc$<br />

sieht folgendermaßen aus:<br />

b<br />

a<br />

b c a<br />

c<br />

a $ b<br />

5<br />

$ b<br />

c<br />

4<br />

c<br />

$<br />

6<br />

$<br />

Definition <strong>4.</strong>6. Es seien Σ ein Alphabet, s, u Zeichenreihen (<strong>String</strong>s)<br />

∈ Σ ∗ mit s ≠ ɛ und i ∈ IN mit 1 ≤ i ≤ |s|.<br />

Dann heißt u Positionsidentifikator für die Position i in s (Bezeichnung:<br />

u = pid s (i)) genau dann, wenn die folgenden Bedingungen gelten:<br />

(i) ∃y, z mit s = yuz und |y| = i − 1.<br />

3<br />

c<br />

$<br />

(ii) ∀y ′ , z ′ : s = y ′ uz ′ ⇒ y ′ = y und z ′ = z<br />

2<br />

$<br />

(iii) |u| ist minimal unter den Bedingungen (i) und (ii).<br />

1<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 242<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 244<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Trie<br />

• Es besteht eine Eins-zu-eins-Beziehung zwischen den Knoten <strong>von</strong><br />

ST (s) und den verschiedenen Substrings <strong>von</strong> s.<br />

• Die Anzahl der Knoten <strong>von</strong> ST (s) ist u.U. quadratisch in |s|.<br />

• Dies ist z.B. für <strong>String</strong>s der Form a m b m $ der Fall. Solche <strong>String</strong>s der<br />

Länge 2m + 1 haben m 2 + 4m + 2 verschiedene Substrings.<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Positionsbäume<br />

Bemerkungen:<br />

• u = ɛ erfüllt Bedingung (i) für jedes s ∈ Σ + und jedes i mit 1 ≤ i ≤ |s|.<br />

• Für |s| ≥ 2 erfüllt u = ɛ Bedingung (ii) nicht.<br />

• Nicht für alle s und alle i existiert ein Positionsidentifikator.<br />

☞ Statt einen kompletten Suffix ab Position i in text nimmt man nur<br />

einen <strong>String</strong> auf, der die Position i in text eindeutig beschreibt.<br />

• Falls für <strong>String</strong> s und Position i ein Positionsidentifikator existiert, so<br />

ist er eindeutig bestimmt.<br />

Lemma <strong>4.</strong>8. Bei Abschluß <strong>von</strong> s mit einem Sonderzeichen $, das in s<br />

nicht vorkommt, existiert für jede Position <strong>von</strong> s$ ein Positionsidentifikator.<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 243<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 245


<strong>4.</strong> <strong>Textalgorithmen</strong> Positionsbäume<br />

Beispiel <strong>4.</strong>16.<br />

Der <strong>String</strong> s = abcabc$ hat die Positionsidentifikatoren:<br />

i pid s (i)<br />

1 abca<br />

2 bca<br />

3 ca<br />

4 abc$<br />

5 bc$<br />

6 c$<br />

7 $<br />

Korollar <strong>4.</strong>9. s 1 . . . s n+1 besitzt je einen Positionsidentifikator für i =<br />

1, . . . , n + 1 ⇐⇒ s n+1 ∉ {s 1 , . . . , s n }<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Positionsbäume<br />

Positionsbaum<br />

Definition <strong>4.</strong>7. Es sei s = s[1] . . . s[n]$ ein <strong>String</strong>. Dann ist der Positionsbaum<br />

P T (s) der Baum<br />

P T (s) = T rie({pid s (1), pid s (2), . . . , pid s (n + 1)})<br />

wobei die Blätter p <strong>von</strong> P T (s) zusätzlich eine Markierung label(p) tragen,<br />

für die gilt:<br />

label(p) = i ⇐⇒ path(p) = pid s (i)<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 246<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 248<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Positionsbäume<br />

Lemma <strong>4.</strong>10. Kein Positionsidentifikator ist Anfang (Präfix) eines Positionsidentifikators<br />

für eine weitere Position in demselben <strong>String</strong> s$.<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Positionsbäume<br />

Beispiel <strong>4.</strong>17.<br />

Der Positionsbaum für s = abcabc$ sieht wie folgt aus:<br />

Beweis.<br />

a<br />

b<br />

c<br />

$<br />

• Sei i ≠ j mit p(i) Anfang <strong>von</strong> p(j),<br />

7<br />

• d.h. p(i)ω = p(j),<br />

⇒ p(i) kommt in s auch an Position j vor.<br />

b c a<br />

3<br />

c<br />

a $<br />

6<br />

$<br />

⇒ p(i) identifiziert die Position i nicht eindeutig.<br />

⇒ Widerspruch.<br />

1<br />

a<br />

4<br />

$<br />

2<br />

5<br />

✷<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 247<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 249


<strong>4.</strong> <strong>Textalgorithmen</strong> Positionsbäume<br />

Lemma <strong>4.</strong>11.<br />

Jeder <strong>String</strong> s$ hat einen eindeutigen Positionsbaum.<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong>-<strong>Matching</strong> mit Positionsbäumen<br />

<strong>String</strong>-<strong>Matching</strong> mit Hilfe <strong>von</strong> Positionsbäumen<br />

Beweis.<br />

• Für jede Position i gibt es einen eindeutigen Positionsidentifikator<br />

p(i).<br />

• Man braucht nun nur den Trie zu p(1), . . . , p(n + 1) aufzubauen.<br />

• Die Eindeutigkeit folgt aus der Minimalität des Baumes.<br />

✷<br />

Korollar <strong>4.</strong>12. Ein Positionsbaum für s$ mit |s| = n hat n + 1 Blätter.<br />

Beweis. Folgt direkt aus Lemma <strong>4.</strong>11. ✷<br />

• Gegeben sei der Positionsbaum für text$.<br />

• Es sei pat = p 1 , . . . , p m .<br />

• Steige im Positionsbaum ab gemäß pat, d.h.<br />

– wähle zunächst die <strong>von</strong> der Wurzel ausgehende und mit p 1 markierte<br />

Kante,<br />

– dann die mit p 2 markierte Kante, usw.<br />

• Es treten nun verschiedene Fälle auf, die den Abstieg beenden.<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 250<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 252<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Positionsbäume<br />

Lemma <strong>4.</strong>13. Es sei s ≠ ɛ. Dann hat jedes Blatt im Positionsbaum <strong>von</strong><br />

s$ mindestens einen Bruder.<br />

Beweis.<br />

• p(i) = ya, y ∈ Σ ∗ , a ∈ Σ<br />

• Falls y = ɛ, so hat das mit i markierte Blatt einen Bruder, weil es<br />

noch mindestens eine weitere Position im Baum gibt.<br />

• Also sei |y| ≥ 1.<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong>-<strong>Matching</strong> mit Positionsbäumen<br />

Der Abstieg terminiert, falls:<br />

(1) pat erschöpft und zwar<br />

(1.1) an einem inneren Knoten oder<br />

(1.2) an einem Blatt<br />

(2) pat nicht erschöpft, aber der Positionsbaum ist erschöpft und zwar<br />

(2.1) an einem inneren Knoten oder<br />

(2.2) an einem Blatt<br />

⇒ Dann kommt y noch an anderer Stelle, etwa j ≠ i, im <strong>String</strong> s vor<br />

(wegen der Minimalität <strong>von</strong> p(i)).<br />

⇒ Also muß es eine Bruderkante geben, die den Anfang des Weges zu<br />

dem mit j markierten Blatt bildet.<br />

✷<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 251<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 253


<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong>-<strong>Matching</strong> mit Positionsbäumen<br />

Aus diesen unterschiedlichen Fällen der Terminierung ergeben sich folgende<br />

Resultate:<br />

(1) pat kommt genau an den Positionen vor, die vom Abbruchknoten aus<br />

erreichbar sind.<br />

Insbesondere kommt im Fall (1.2) pat genau an der Position vor, mit<br />

der das Blatt markiert ist.<br />

(2.1) pat kommt in text nicht vor.<br />

(2.2) pat kommt höchstens an der Position vor, mit der das erreichte Blatt<br />

markiert ist.<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong>-<strong>Matching</strong> mit Positionsbäumen<br />

Komplexität:<br />

• Der Aufwand für die Suche beträgt O(|pat|),<br />

• mit Ausnahme <strong>von</strong> Fall 1.1, wo ein Unterbaum traversiert werden<br />

muß.<br />

• Es liegt die Vermutung nahe, daß im Falle 1.1 der Aufwand O(|pat| +<br />

k) beträgt, wobei k die Anzahl der Vorkommen <strong>von</strong> pat ist.<br />

• Alternative: man ermittelt für jeden Knoten (oder jeden ab einer gewissen<br />

Tiefe) die Marken der <strong>von</strong> diesem Knoten aus erreichbaren<br />

Blätter und legt diese als Liste in dem Knoten ab.<br />

Nachteil: erhöhter Speicherplatzverbrauch<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 254<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 256<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong>-<strong>Matching</strong> mit Positionsbäumen<br />

Beispiel <strong>4.</strong>18. Es sei text =<br />

abcabc.<br />

• pat = bc: Fall 1.1, pat kommt<br />

genau ab den Stellen 2 und 5<br />

vor.<br />

• pat = ca: Fall 1.2, pat kommt<br />

nur an der Stelle 3 vor.<br />

• pat = ac: Fall 2.1, pat kommt in<br />

text nicht vor.<br />

• pat = bcaa: Fall 2.2, pat kann<br />

höchstens an Position 2 vorkommen.<br />

Prüfung ergibt: pat<br />

kommt nicht vor.<br />

c<br />

a<br />

b c a<br />

a<br />

b<br />

$<br />

3<br />

c<br />

$<br />

6<br />

$<br />

7<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> <strong>String</strong>-<strong>Matching</strong> mit Positionsbäumen<br />

• Der Aufwand für das Preprocessing (Konstruktion des Positionsbaums)<br />

wurde bisher nicht berücksichtigt.<br />

• Selbst ein hoher Preprocessingaufwand würde sich aber u.U. lohnen,<br />

wenn:<br />

– genügend Anfragen an text gestellt werden oder<br />

– wenn der Aufwand zu verschiedenen Zeiten verschieden teuer ist.<br />

• pat = cab: Fall 2.2, pat kann<br />

höchstens an Position 3 vorkommen.<br />

Prüfung ergibt: pat<br />

kommt vor.<br />

1<br />

a<br />

4<br />

$<br />

2<br />

5<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 255<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 257


<strong>4.</strong> <strong>Textalgorithmen</strong> Konstruktion <strong>von</strong> Positionsbäumen<br />

Ansätze zur Konstruktion <strong>von</strong> Positionsbäumen<br />

Grundlegender Ansatz: Induktive Konstruktion, d.h. man konstruiert<br />

sukzessive P T (s 1 ), . . . , P T (s 1 , . . . s i ), P T (s 1 . . . s i s i+1 ), . . . , P T (s 1 . . . s n $).<br />

Problem: Die Zwischenbäume P T (s 1 . . . s i ) brauchen nicht zu existieren.<br />

Beispiel: s = abab$.<br />

P T (s 1 s 2 s 3 ) existiert nicht, da Position 3 keinen Pos.-Id. in aba hat.<br />

Wie kann man diese Schwierigkeiten umgehen<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Konstruktion <strong>von</strong> Positionsbäumen<br />

(c) Man spiegelt den Text und die Definition des Begriffs Pos.-Id. und<br />

geht wie unter (a) vor.<br />

Nachteil: unnatürliche Umgehensweise<br />

(d) Man verzichtet darauf, daß die Zwischenbäume Positionsbäume<br />

sind.<br />

☞ Wir untersuchen die Ansätze (a) und (d).<br />

• (a) : Rechts-Links-Konstruktion<br />

• (b) : Links-Rechts-Konstruktion<br />

(a) Man konstruiert den Baum <strong>von</strong> rechts nach links (anstatt <strong>von</strong> links<br />

nach rechts), d.h.<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 258<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 260<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Konstruktion <strong>von</strong> Positionsbäumen<br />

P T n+1 = P T ($)<br />

P T n = P T (s n $)<br />

P T n−1 = P T (s n−1 s n $)<br />

.<br />

P T i = P T (s i . . . s n $)<br />

.<br />

P T 1 = P T (s 1 . . . s n $)<br />

(b) Man hängt jeweils ein $-Zeichen an, d.h.<br />

P T (s 1 $)<br />

P T (s 1 s 2 $)<br />

.<br />

P T (s 1 s 2 . . . s n $)<br />

Nachteil: sehr hoher Aufwand<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Konstruktion <strong>von</strong> Positionsbäumen<br />

Rechts-Links-Konstruktion <strong>von</strong> Positionsbäumen<br />

Bemerkung: Für jeden Teilstring s i . . . s n $ existiert der zugehörige Positionsbaum.<br />

Ausgehend <strong>von</strong> dem Positionsbaum für s i . . . s n $ konstruiert man den<br />

Positionsbaum für s i−1 s i . . . s n $ wie folgt:<br />

Von der Wurzel aus durchläuft man einen Pfad mit den Markierungen<br />

s i−1 , s i , . . ., bis man:<br />

(a) ein Blatt k erreicht.<br />

– Das Blatt k sei mit j markiert.<br />

– Der bisherige Positionsidentifikator für j kommt also auch an der<br />

Stelle i − 1 vor.<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 259<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 261


<strong>4.</strong> <strong>Textalgorithmen</strong> Konstruktion <strong>von</strong> Positionsbäumen<br />

– Sowohl p(j) als auch p(i − 1) sind zu verlängern, bis sie sich unterscheiden.<br />

– Dementsprechend wird der Pfad <strong>von</strong> dem erreichten Blatt aus<br />

verlängert.<br />

– Es entstehen zwei neue Blätter mit den Markierungen i − 1 und j.<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Konstruktion <strong>von</strong> Positionsbäumen<br />

Veranschaulichung für (b):<br />

PT(bcabc$)<br />

PT(bbcabc$)<br />

a<br />

$<br />

a<br />

$<br />

b<br />

c<br />

b<br />

c<br />

4<br />

7<br />

4<br />

7<br />

(b) einen inneren Knoten k erreicht, <strong>von</strong> dem aus keine Kante mit der<br />

benötigten Markierung ausgeht.<br />

c<br />

a<br />

3<br />

6<br />

$<br />

1<br />

b<br />

c<br />

a<br />

3<br />

6<br />

$<br />

– Es ist ein neues Blatt k ′ an k anzuhängen.<br />

– Dieses Blatt erhält die Markierung i − 1.<br />

– Die Kantenmarkierung zur Kante (k, k ′ ) ist das letzte Zeichen <strong>von</strong><br />

p(i − 1).<br />

a<br />

2<br />

$<br />

5<br />

a<br />

2<br />

$<br />

5<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 262<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 264<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Konstruktion <strong>von</strong> Positionsbäumen<br />

Veranschaulichung für (a):<br />

PT(bcabc$)<br />

PT(abcabc$)<br />

a<br />

$<br />

a<br />

$<br />

b<br />

c<br />

b<br />

c<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Konstruktion <strong>von</strong> Positionsbäumen<br />

• Ein Positionsbaum wächst also <strong>von</strong> einem Blatt aus weiter nach unten,<br />

– bis sich eine Unterscheidung der Positionsidentifikatoren ergibt<br />

– und der Pfad sich in zwei Blätter aufspaltet,<br />

4<br />

c<br />

a<br />

$<br />

7<br />

b c a<br />

$<br />

7<br />

• oder <strong>von</strong> einem Zwischenknoten wächst ein neues Blatt heraus.<br />

a<br />

$<br />

3<br />

6<br />

c<br />

a<br />

$<br />

3<br />

6<br />

Aufwandsabschätzung:<br />

2<br />

5<br />

1<br />

a<br />

4<br />

$<br />

2<br />

5<br />

• Der Gesamtaufwand zur Konstruktion eines Positionsbaums für<br />

einen <strong>String</strong> der Länge n ist:<br />

O(<br />

n∑<br />

|p(i)|)<br />

i=1<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 263<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 265


<strong>4.</strong> <strong>Textalgorithmen</strong> Konstruktion <strong>von</strong> Positionsbäumen<br />

Bemerkungen:<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Konstruktion <strong>von</strong> Positionsbäumen<br />

Beispiel <strong>4.</strong>19.<br />

Der partielle Positionsbaum für abba sieht wie folgt aus:<br />

• Nachteil der Rechts-Links-Konstruktion: Die gesamte Zeichenreihe<br />

muß bekannt sein, bevor sich der Positionsbaum konstruieren läßt.<br />

• Beim Entwickeln <strong>von</strong> Text möchte man aber u.U. schon vorher Textstellen<br />

identifizieren und Korrekturen vornehmen.<br />

a<br />

b<br />

• Einen Positionsbaum für den umgekehrten Text aufzubauen wäre unnatürlich.<br />

b<br />

a<br />

b<br />

• Kann ein Positionsbaum auch effizient <strong>von</strong> links nach rechts konstruiert<br />

werden<br />

1 3<br />

2<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 266<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 268<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Konstruktion <strong>von</strong> Positionsbäumen<br />

Links-Rechts-Konstruktion <strong>von</strong> Positionsbäumen<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Konstruktion <strong>von</strong> Positionsbäumen<br />

• Für Positionsbäume gilt eine sehr einfache und wichtige Verschachtelungseigenschaft,<br />

die beschreibt, wie sich Positionsidentifikatoren<br />

überlappen können.<br />

• Ziel ist es, ein sogenanntes on-line Verfahren für die Konstruktion <strong>von</strong><br />

Positionsbäumen zu entwickeln.<br />

• Da für Teilstrings der Positionsbaum nicht existiert, muß man partielle<br />

Positionsbäume betrachten.<br />

• Ein partieller Positionsbaum enthält für genau diejenigen Positionen<br />

des Textes einen Positionsidentifikator, für die es einen solchen Positionsidentifikator<br />

gibt.<br />

• Die wesentliche Aussage hierzu liefert das folgende Monotonie-<br />

Lemma.<br />

Lemma <strong>4.</strong>1<strong>4.</strong> [Monotonie-Lemma] Es sei e(i) die Position, bei der<br />

der Positionsidentifikator p(i) endet. Dann gilt:<br />

i < j =⇒ e(i) ≤ e(j)<br />

Beweis. Annahme: Es gibt in einem <strong>String</strong> s Positionen i < j mit e(i) ><br />

e(j).<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 267<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 269


<strong>4.</strong> <strong>Textalgorithmen</strong> Konstruktion <strong>von</strong> Positionsbäumen<br />

Veranschaulichung:<br />

s<br />

i j e(j) e(i)<br />

• p(i) = s i . . . s e(i) ist der kürzestmögliche <strong>String</strong>, der die Position i<br />

eindeutig bestimmt.<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Konstruktion <strong>von</strong> Positionsbäumen<br />

Prinzip der Links-Rechts-Konstruktion<br />

• Man nehme an, wir haben den partiellen Positionsbaum für den<br />

<strong>String</strong> s 1 . . . s i .<br />

• k sei die erste Postion, für die kein Positionsidentifikator existiert, d.h.<br />

s k s k+1 . . . s i ist nicht eindeutig in s 1 . . . s i .<br />

• Somit kommt s i+1 . . . s e(i)−1 noch an anderer Stelle vor.<br />

s(1)s(2) . . . s(k−1) s(k) . . .<br />

s(i)<br />

• s j . . . s e(j) ist Substring <strong>von</strong> s i+1 . . . s e(i)−1 .<br />

• Somit ist s j . . . s e(j) nicht eindeutig in s. Widerspruch.<br />

✷<br />

• Der Anfang <strong>von</strong> p(k) ist s k s k+1 . . . s i .<br />

• Diesem Anfang entspricht ein Knoten im partiellen Positionsbaum.<br />

Dieser Knoten sei mit k markiert.<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 270<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 272<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Konstruktion <strong>von</strong> Positionsbäumen<br />

• Wir fassen Intervalle <strong>von</strong> Positionen zusammen, deren Pos.-Id. an<br />

derselben Stelle enden. Dies deuten wir so an:<br />

text<br />

Intervalle <strong>von</strong><br />

Positionen<br />

die hier<br />

enden<br />

• Graphisch dargestellt ergibt sich dann die folgende Verschachtelung<br />

für die Positionsidentifikatoren:<br />

text<br />

$<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Konstruktion <strong>von</strong> Positionsbäumen<br />

Konstruktionsschritt:<br />

• Bei der Hinzunahme <strong>von</strong> s i+1 wird nun versucht, die Markierung<br />

im partiellen Positionsbaum weiter nach unten zu verschieben.<br />

• Hierbei können die folgenden Fälle auftreten:<br />

(1) Der mit k markierte Knoten verfügt über eine ausgehende Kante<br />

für s i+1 .<br />

Dann bewegt man k längs der mit s i+1 markierten Kante. Jetzt<br />

können die folgenden Fälle auftreten:<br />

(1.1) Der jetzt mit k markierte Knoten ist ein innerer Knoten.<br />

Dann ist nichts weiter zu tun.<br />

(1.2) Der jetzt mit k markierte Knoten ist ein Blatt mit Markierung l.<br />

k<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 271<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 273


<strong>4.</strong> <strong>Textalgorithmen</strong> Konstruktion <strong>von</strong> Positionsbäumen<br />

Dann ist p(l) um ein Zeichen zu verlängern, d.h. an das bisherige<br />

Blatt wird ein neuer Knoten angehängt und die Markierung l<br />

wandert zu dem neuen Blatt.<br />

(2) Der mit k markierte Knoten hat keine ausgehende Kante, die mit<br />

s i+1 markiert ist.<br />

Dann wird ein neues Blatt und eine neue ausgehende und mit s i+1<br />

markierte Kante zu diesem Blatt angelegt.<br />

Das neue Blatt wird mit k markiert.<br />

Es gilt nun zu prüfen, ob Positionsidentifikatoren für die Positionen<br />

k + 1, k + 2, . . . existieren. Hierzu sind neue Blätter in den Baum<br />

einzutragen.<br />

Es sei k + m die erste Position für die nun kein Pos.-Id. existiert.<br />

Dann ist noch k + m einzutragen.<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Konstruktion <strong>von</strong> Positionsbäumen<br />

Fall 1.2:<br />

s(i)<br />

s(i)<br />

k<br />

s(i+1)<br />

s(i+1)<br />

l<br />

k<br />

s( e(l) )<br />

l<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 274<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 276<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Konstruktion <strong>von</strong> Positionsbäumen<br />

Fall 1.1:<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Konstruktion <strong>von</strong> Positionsbäumen<br />

Fall 2:<br />

s(i)<br />

s(i)<br />

s(i)<br />

s(i)<br />

k<br />

k<br />

s(i+1)<br />

s(i+1)<br />

s(i+1)<br />

k<br />

k<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 275<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 277


<strong>4.</strong> <strong>Textalgorithmen</strong> Konstruktion <strong>von</strong> Positionsbäumen<br />

Bemerkungen:<br />

• Man kann den Eingabestring jederzeit mit einem $ abschließen.<br />

Dann wird mit dem angegebenen Verfahren der zugehörige Positionsbaum<br />

fertig konstruiert.<br />

• Folgende Kosten treten bei der Konstruktion auf:<br />

(1.1) p(k) wird um ein Zeichen verlängert.<br />

Aufwand: O(1)<br />

(1.2) p(k) und p(l) werden um je ein Zeichen verlängert.<br />

Aufwand: O(1)<br />

(2) p(k) wird um ein Zeichen verlängert.<br />

Aufwand: O(1)<br />

Das Einfügen <strong>von</strong> k + 1, k + 2, . . . k + m − 1 und das Auffinden <strong>von</strong><br />

k + m<br />

kostet proportional zur Länge <strong>von</strong> p(k + 1) . . . p(k + m).<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 278<br />

<strong>4.</strong> <strong>Textalgorithmen</strong> Konstruktion <strong>von</strong> Positionsbäumen<br />

• Der Gesamtaufwand zum Aufbau eines Positoinsbaums für einen<br />

<strong>String</strong> der Länge n ist also:<br />

O(<br />

n∑<br />

|p(i)|)<br />

i=1<br />

• Damit ist die Links-Rechts-Konstruktion nicht aufwendiger als die<br />

Rechts-Links-Konstruktion.<br />

• Sprachliche Texte s haben häufig die Eigenschaft, daß |p(i)| ≈ log |s|<br />

gilt. Dann ergibt sich für den Gesamtaufwand:<br />

O(|s| log |s|)<br />

• Im Worst-Case beträgt der Aufwand O(n 2 ).<br />

Information Retrieval — FH Bonn-Rhein-Sieg, SS 06 279

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!