4. Textalgorithmen Arten von String-Matching-Problemen ...
4. Textalgorithmen Arten von String-Matching-Problemen ...
4. Textalgorithmen Arten von String-Matching-Problemen ...
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