Algorithmen auf Sequenzen Volltext-Indizes Dominik Kopczynski Lehrstuhl für Algorithm Engineering (LS11) Fakultät für Informatik TU Dortmund Überblick Bei wiederholten Suchen auf (langen) Texten, ist es sinnvoll den Text vorzuverarbeiten. Durch Indizierung des Textes kann die Laufzeit so gesenkt werden, dass sie nur noch von der Länge des Patterns abhängt, also O(m). Bei natürlichsprachlichen Texten können Wort-basierte Indizes eingesetzt werden. Indizes, die die Suche nach beliebigen Teilstrings (auch wortübergreifend) erlauben, werden Volltext-Indizes genannt. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 2 Überblick Bei wiederholten Suchen auf (langen) Texten, ist es sinnvoll den Text vorzuverarbeiten. Durch Indizierung des Textes kann die Laufzeit so gesenkt werden, dass sie nur noch von der Länge des Patterns abhängt, also O(m). Bei natürlichsprachlichen Texten können Wort-basierte Indizes eingesetzt werden. Indizes, die die Suche nach beliebigen Teilstrings (auch wortübergreifend) erlauben, werden Volltext-Indizes genannt. Im Folgenden wird ein Alphabet konstanter Größe angenommen, also O(1). D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 2 Überblick n $ $ 5 $ 4 s $ 0 1 6 $ 0 ananas$ 2 anas$ 4 as$ 1 nanas$ 3 nas$ 5 s$ 3 s$ s $ nas$ a s$ a nas$ s s$ n s$ 6 s na $ a na a s a n s a $ n 2 $ananas ananas$ anas$an as$anan nanas$a nas$ana s$anana $ Suffix-Trie Suffix-Tree Suffix-Array D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes FM-Index 3 Grundidee der Volltext-Indizes Indizierung sämtlicher Suffixe eines Textes: mississippi ississippi ssissippi sissippi issippi ssippi sippi ippi ppi pi i D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 4 Grundidee der Volltext-Indizes Indizierung sämtlicher Suffixe eines Textes: mississippi ississippi ssissippi sissippi issippi ssippi sippi ippi ppi Quadratisch viele pi (n · (n + 1))/2 Zeichen, i wenn alle Suffixe betrachtet werden. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 4 Suffix-Trie Alle Suffixe sollen in einen Trie hinzugefügt werden, ähnlich wie beim Aho-Corasick. Solch ein Trie sollte folgende Eigenschaften erfüllen: Es gibt eine Bijektion zwischen den den Blättern des Baums und den Suffixen des Textes. Um diese Eigenschaft zu gewährleisten, wird im Folgenden ein Wächter (Sentinel) $ am Ende des Textes eingeführt, für den folgende Eigenschaften gelten: Σ ∩ $ = ∅, für alle σ ∈ Σ : $ < σ. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 5 Suffix-Trie Suffix-Trie für den Text T = aaa mit und ohne Sentinal: a $ a $ a a $ a a $ D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 6 Alphabet-Abhängigkeit Ist die Alphabetgröße nicht konstant, hängt die Laufzeit von der Datenstruktur ab, mit der Kinderknoten gesucht werden, sei dabei cv ∈ O(|Σ|) die Anzahl der Kinder von Knoten v . Datenstruktur Verkettete Liste Balancierter Baum Array Größe |Σ| Perfektes Hashing1 1 Laufzeit O(cv ) O(log cv ) O(1) O(1) Platz pro Knoten O(cv ) O(cv ) O(|Σ|) O(cv ) Platz gesamt O(n) O(n) O(n|Σ|) O(n) theoretisch möglich D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 7 Suffix-Trie Mustersuche: Jeder Teilstring des Textes T ist auch Präfix eines Suffixes von T . Die Mustersuche beginnt an der Wurzel. Der Trie wird Zeichen für Zeichen des Patterns durchtraversiert, bis entweder das komplette Pattern erkannt wurde (match), oder von einem Knoten aus keine ausgehende Kante P[j] vorhanden ist (mismatch). D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 8 Suffix-Trie $ Beispiel-Trie für den Text T = ananas$: n a n a s s a n s a $ n s a $ $ s $ s $ $ D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 9 Implementierung 1 2 3 4 5 6 7 8 9 def build_trie(T): root, n = dict(), len(T) for t in [T[j:] for j in range(n)]: node = root for c in t: if c not in node: node[c] = dict() node = node[c] return root 10 11 12 13 14 15 def has_pattern(node, P): for c in P: if c not in node: return False node = node[c] return True D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 10 Zusammenfassung Mit Hilfe des Suffix-Tries kann eine Patternsuche in O(m) durchgeführt werden. Sowohl die Laufzeit für der Erstellung des Suffix-Tries als auch der Speicherverbrauch beträgt O(n2 ). Ein Suffix-Trie ist eine naive Implementierung eines Volltext-Indexes und wird aufgrund seines hohen Speicherverbrauchs nicht genutzt. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 11 Beobachtungen Der Trie kann lediglich das Vorkommen von Pattern im Text feststellen, jedoch nicht die Position. Im Suffix-Trie kommen oft Ketten von Knoten vor, die nur einen Nachfolger haben. Bei der Erstellung des Tries wird der i-te Buchstabe i + 1 mal betrachtet. Wünschenswert wäre, wenn dies nur einmal geschähen würde. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 12 Beobachtungen Der Trie kann lediglich das Vorkommen von Pattern im Text feststellen, jedoch nicht die Position. Im Suffix-Trie kommen oft Ketten von Knoten vor, die nur einen Nachfolger haben. Bei der Erstellung des Tries wird der i-te Buchstabe i + 1 mal betrachtet. Wünschenswert wäre, wenn dies nur einmal geschähen würde. All diese Verbesserungen realisiert der Suffix-Tree. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 12 Σ+ -Baum Definition (Σ+ -Baum) Sei Σ ein Alphabet, dann ist ein Σ+ -Baum ein gewurzelter Baum, dessen Kanten jeweils mit einem nichtleeren String über Σ annotiert sind, so dass kein Knoten zwei ausgehende Kanten hat, die mit dem gleichen Buchstaben beginnen. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 13 Σ+ -Baum Definition (Σ+ -Baum) Sei Σ ein Alphabet, dann ist ein Σ+ -Baum ein gewurzelter Baum, dessen Kanten jeweils mit einem nichtleeren String über Σ annotiert sind, so dass kein Knoten zwei ausgehende Kanten hat, die mit dem gleichen Buchstaben beginnen. Definition (Kompakter Σ+ -Baum) Ein Σ+ -Baum heißt kompakt, wenn kein Knoten (außer ggf. der Wurzel) genau ein Kind besitzt. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 13 (String)Tiefe eines Baums Definition (Tiefe eines Baums) Die Tiefe dep(s) eines Knotens s ist die Anzahl der Kanten eines eindeutig beschrifteten Pfades von der Wurzel zu s. Die Wurzel r hat per Definition dep(r ) := 0. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 14 (String)Tiefe eines Baums Definition (Tiefe eines Baums) Die Tiefe dep(s) eines Knotens s ist die Anzahl der Kanten eines eindeutig beschrifteten Pfades von der Wurzel zu s. Die Wurzel r hat per Definition dep(r ) := 0. Definition (Stringtiefe eines Suffix-Trees) Für Knoten s sei str(s) die Konkatenation der Kantenbeschriftungen auf einem eindeutigen Pfad von der Wurzel zu s. Dabei sei die Stringtiefe strdep(s) = |str(s)|. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 14 (String)Tiefe eines Baums Definition (Tiefe eines Baums) Die Tiefe dep(s) eines Knotens s ist die Anzahl der Kanten eines eindeutig beschrifteten Pfades von der Wurzel zu s. Die Wurzel r hat per Definition dep(r ) := 0. Definition (Stringtiefe eines Suffix-Trees) Für Knoten s sei str(s) die Konkatenation der Kantenbeschriftungen auf einem eindeutigen Pfad von der Wurzel zu s. Dabei sei die Stringtiefe strdep(s) = |str(s)|. Die Tiefe eines Knotens s muss nicht der Stringtiefe entsprechen. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 14 Suffix-Tree Ein Suffix-Tree soll folgende Eigenschaften für einen Text T $ erfüllen: Es gibt eine Bijektion zwischen den Blättern der Trees und den Suffixen des Textes. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 15 Suffix-Tree Ein Suffix-Tree soll folgende Eigenschaften für einen Text T $ erfüllen: Es gibt eine Bijektion zwischen den Blättern der Trees und den Suffixen des Textes. Die Kanten des Baums sind mit nicht-leeren Teilstrings von T $ annotiert. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 15 Suffix-Tree Ein Suffix-Tree soll folgende Eigenschaften für einen Text T $ erfüllen: Es gibt eine Bijektion zwischen den Blättern der Trees und den Suffixen des Textes. Die Kanten des Baums sind mit nicht-leeren Teilstrings von T $ annotiert. Ausgehende Kanten eines Knotens beginnen mit verschiedenen Buchstaben. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 15 Suffix-Tree Ein Suffix-Tree soll folgende Eigenschaften für einen Text T $ erfüllen: Es gibt eine Bijektion zwischen den Blättern der Trees und den Suffixen des Textes. Die Kanten des Baums sind mit nicht-leeren Teilstrings von T $ annotiert. Ausgehende Kanten eines Knotens beginnen mit verschiedenen Buchstaben. Jeder innere Knoten hat ≥ 2 Kinder. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 15 Suffix-Tree Ein Suffix-Tree soll folgende Eigenschaften für einen Text T $ erfüllen: Es gibt eine Bijektion zwischen den Blättern der Trees und den Suffixen des Textes. Die Kanten des Baums sind mit nicht-leeren Teilstrings von T $ annotiert. Ausgehende Kanten eines Knotens beginnen mit verschiedenen Buchstaben. Jeder innere Knoten hat ≥ 2 Kinder. Jeder Teilsting von T $ kann auf einem Pfad von der Wurzel abgelesen werden. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 15 Suffix-Tree ] [6 :7 a [0 :1] $ ] :7 [5 s$ 1:3] na [ Beispiel-Tree für den Text T = ananas$: 5 [1: na 4 7] [5: s$ nas$ [3:7] s$ [5:7] 3] 6 1 3 7] [5: s$ nas$ [3:7] 0 2 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 16 Eigenschaften des Suffix-Tree Der Speicherverbrauch pro Knoten sinkt auf O(1), da nur noch das Indexpaar (b, e) und die Nachkommen des Knotens gespeichert werden. Das Paar (b, e) entspricht dem Teilstring T [b : e]. Da es genau n Blätter gibt und jeder innere Knoten mind. zwei Kinder hat, ist die Anzahl der inneren Knoten auf n beschränkt. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 17 Eigenschaften des Suffix-Tree Der Speicherverbrauch pro Knoten sinkt auf O(1), da nur noch das Indexpaar (b, e) und die Nachkommen des Knotens gespeichert werden. Das Paar (b, e) entspricht dem Teilstring T [b : e]. Da es genau n Blätter gibt und jeder innere Knoten mind. zwei Kinder hat, ist die Anzahl der inneren Knoten auf n beschränkt. Der Speicherverbrauch liegt somit bei O(n). Durch die Nummerierung der Blätter mit der Startposition ihres entsprechenden Suffixes lässt sich bei einer Mustersuche die Position im Text bestimmen. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 17 Konstruktion des Suffix-Trees Beim naiven Ansatz behilft man sich eines Suffix-Tries, den man zum Suffix-Tree erweitert. Durch Zählen der Knoten im Pfad zu den Blättern, kann man diese beschriften und die Kanten indizieren. Die Erweiterung lässt sich in O(n2 ) realisieren, die Gesamtlaufzeit beträgt also immer noch O(n2 ). D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 18 Konstruktion des Suffix-Trees Beim naiven Ansatz behilft man sich eines Suffix-Tries, den man zum Suffix-Tree erweitert. Durch Zählen der Knoten im Pfad zu den Blättern, kann man diese beschriften und die Kanten indizieren. Die Erweiterung lässt sich in O(n2 ) realisieren, die Gesamtlaufzeit beträgt also immer noch O(n2 ). Umso bemerkenswerter ist es, dass der Ukkonen-Algorithmus zur Konstruktion eines Suffix-Trees eine Laufzeit von O(n) hat. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 18 Ukkonen-Algorithmus für Suffix-Trees Der Algorithmus konstruiert zu einem Text T $ mit n := |T $| einen Suffix-Tree in n Iterationen 0, 1, . . . , n − 1. In jeder Iteration i wird das Zeichen T [i] zum Suffix-Tree hinzugefügt. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 19 Ukkonen-Algorithmus für Suffix-Trees Der Algorithmus konstruiert zu einem Text T $ mit n := |T $| einen Suffix-Tree in n Iterationen 0, 1, . . . , n − 1. In jeder Iteration i wird das Zeichen T [i] zum Suffix-Tree hinzugefügt. Der Algorithmus ist ein online-Algorithmus, das bedeutet, dass nach jeder Iteration i der Baum ein (gültiger) Suffix-Tree für das Präfix T [: i + 1] ist. Um Linearlaufzeit zu erreichen, darf jede Iteration nur amortisiert O(1) dauern. Mit verschiedenen Tricks lässt sich diese Laufzeit erreichen. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 19 Tricks des Ukkonen-Algorithmus 1 Es wird das Tupel (s, k) verwaltet, wobei k der Anzahl der Buchstaben an allen bereits beschrittenen Kanten und s dem aktiven Knoten entspricht. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 20 Tricks des Ukkonen-Algorithmus 1 Es wird das Tupel (s, k) verwaltet, wobei k der Anzahl der Buchstaben an allen bereits beschrittenen Kanten und s dem aktiven Knoten entspricht. 2 Einmal eingeführte Blattknoten können nicht mehr erreicht werden. Das Indexpaar zu dem Blattknoten wird mit (b, ∞) beschriftet. So wird das Ende automatisch weitergeführt. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 20 Tricks des Ukkonen-Algorithmus 1 Es wird das Tupel (s, k) verwaltet, wobei k der Anzahl der Buchstaben an allen bereits beschrittenen Kanten und s dem aktiven Knoten entspricht. 2 Einmal eingeführte Blattknoten können nicht mehr erreicht werden. Das Indexpaar zu dem Blattknoten wird mit (b, ∞) beschriftet. So wird das Ende automatisch weitergeführt. 3 Um vom Knoten s mit str(s) = ax und a ∈ Σ, x ∈ Σ+ zum Knoten w mit str(w ) = x in konstanter Zeit zu gelangen, werden Verbindungen (Suffixlinks) eingesetzt. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 20 Tricks des Ukkonen-Algorithmus 1 Es wird das Tupel (s, k) verwaltet, wobei k der Anzahl der Buchstaben an allen bereits beschrittenen Kanten und s dem aktiven Knoten entspricht. 2 Einmal eingeführte Blattknoten können nicht mehr erreicht werden. Das Indexpaar zu dem Blattknoten wird mit (b, ∞) beschriftet. So wird das Ende automatisch weitergeführt. 3 Um vom Knoten s mit str(s) = ax und a ∈ Σ, x ∈ Σ+ zum Knoten w mit str(w ) = x in konstanter Zeit zu gelangen, werden Verbindungen (Suffixlinks) eingesetzt. 4 Beim Traversieren des Baums können Kanten mit bekanntem Indexpaar (b, e) in konstanter Zeit übersprungen werden. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 20 Implementierung Die Klasse STNode verwaltet die Knoten im Suffix-Tree S mit folgenden Attributen: Ein Dictionary targets : c → (s, b, e) mit c := T [b], s ∈ nodes(S), 0 ≤ b < e ≤ n zum Speichern der Kinderknoten und Indexpaaren (b, e) auf den hinführenden Kanten. Eine Referenz s link auf das längste Suffix. Startposition pos des Suffixes im Text. 1 2 3 4 5 class STNode(): def __init__(self, s_link = None, pos= -1): self.targets = dict() self.s_link = s_link self.pos = pos D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 21 Implementierung Vorbereitung: Der Algorithmus startet mit einem leeren Baum, bestehend aus einer Wurzel root. Da root selber ein sich ändernder Knoten ist, muss es noch einen Knoten top oberhalb der Wurzel geben, der nur für die Konstruktion relevant ist. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 22 Implementierung Vorbereitung: Der Algorithmus startet mit einem leeren Baum, bestehend aus einer Wurzel root. Da root selber ein sich ändernder Knoten ist, muss es noch einen Knoten top oberhalb der Wurzel geben, der nur für die Konstruktion relevant ist. Von top aus soll root beim Lesen aller Zeichen aus dem Alphabet erreichbar sein. Der Suffixlink von root zeigt auf top. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 22 Implementierung Vorbereitung: Der Algorithmus startet mit einem leeren Baum, bestehend aus einer Wurzel root. Da root selber ein sich ändernder Knoten ist, muss es noch einen Knoten top oberhalb der Wurzel geben, der nur für die Konstruktion relevant ist. Von top aus soll root beim Lesen aller Zeichen aus dem Alphabet erreichbar sein. Der Suffixlink von root zeigt auf top. Es wird von der Wurzel gestartet. Mit der Einfüge-Operation update sollen in jeder Iteration die Zeichen aus T in den Baum eingefügt werden. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 22 Implementierung 1 2 3 4 5 6 def ukkonen(T): top = STNode() root = STNode(s_link = top) for c in T: if c not in top.targets: top.targets[c] = (root, -1, 0) 7 8 9 10 11 s, k, pos = root, 0, 0 for i in range(len(T)): s, k, pos = update(s, k, i, T, pos) return root D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 23 Implementierung Eine aktuelle Position kann sich entweder direkt an einem inneren Knoten befinden, oder an einer Kante, die von einem Knoten ausgeht. Wenn k = i, dann befindet sich die aktuelle Position an einem Knoten. Wenn k < i, dann befindet sich die aktuelle Position an einer Kante. Dabei sei s der ausgehende Knoten. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 24 Implementierung Eine aktuelle Position kann sich entweder direkt an einem inneren Knoten befinden, oder an einer Kante, die von einem Knoten ausgeht. Wenn k = i, dann befindet sich die aktuelle Position an einem Knoten. Wenn k < i, dann befindet sich die aktuelle Position an einer Kante. Dabei sei s der ausgehende Knoten. i z }| { Beispiel: T = ...xyzabc...xyz ab ...: | {z } k x y z s a b c D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 24 Implementierung Definition (Referenz) Sei w ein Teilstring von T [: i] mit w = uv , wobei u = T [j : b] und v = T [b : e]. Ist str(s) = u dann ist (s, (b, e)) eine Referenz auf w . D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 25 Implementierung Definition (Referenz) Sei w ein Teilstring von T [: i] mit w = uv , wobei u = T [j : b] und v = T [b : e]. Ist str(s) = u dann ist (s, (b, e)) eine Referenz auf w . abcde Beispiel: w = = b (abc, (6, 8)) = b (a, (4, 8)) = b (, (3, 8)) 34567 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 25 Implementierung Definition (Referenz) Sei w ein Teilstring von T [: i] mit w = uv , wobei u = T [j : b] und v = T [b : e]. Ist str(s) = u dann ist (s, (b, e)) eine Referenz auf w . abcde Beispiel: w = = b (abc, (6, 8)) = b (a, (4, 8)) = b (, (3, 8)) 34567 Definition (Kanonische Referenz) Eine Referenz (s, (b, e)) heißt kanonisch, wenn e − b minimal für alle Referenzen auf w ist. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 25 Implementierung Beim Update wird zuerst überprüft, ob der aktuell einzufügende Buchstabe von der aktuellen Position gelesen werden kann. Dabei muss unterschieden werden, ob die aktuelle Position sich in einem Knoten befindet oder nicht. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 26 Implementierung Beim Update wird zuerst überprüft, ob der aktuell einzufügende Buchstabe von der aktuellen Position gelesen werden kann. Dabei muss unterschieden werden, ob die aktuelle Position sich in einem Knoten befindet oder nicht. Wenn sich die aktuelle Position an einer Kante befindet, und der nächste Buchstabe nicht gelesen werden kann, wird ein innerer Knoten hinzugefügt. Diese Schritte werden in der Funktion test and split realisiert. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 26 Implementierung 1 2 3 def test_and_split(s, k, i, T): if k == i: return T[i] in s.targets, s 4 5 6 7 8 s1, b, e = active = i if T[i] == return s.targets[T[k]] - k + b T[active]: True, s 9 10 11 12 13 r = STNode() s.targets[T[b]] = (r, b, active) r.targets[T[active]] = (s1, active, e) return False, r D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 27 Implementierung Während eines Updatevorgangs kommt es vor, dass von der aktuellen Position auf einen inneren Knoten geringerer Tiefe gewechselt wird. Um zu wahren, dass die Referenz (s, (k, i)) wieder kanonisch wird, wird die Funktion canonize aufgerufen. Beispiel: T = ...abcdefghi...abcdefgh... D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 28 Implementierung Während eines Updatevorgangs kommt es vor, dass von der aktuellen Position auf einen inneren Knoten geringerer Tiefe gewechselt wird. Um zu wahren, dass die Referenz (s, (k, i)) wieder kanonisch wird, wird die Funktion canonize aufgerufen. Beispiel: T = ...abcdefghi...abcdefgh... ↑k 0 ↑k ↑i fgh i e cd i fgh D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes cd e ab ab (k 0 , i) → (k, i) 28 Implementierung 1 2 3 4 5 6 7 8 def canonize(s, k, i, T): if i < k: return s, k s1, b, e = s.targets[T[k]] while e - b <= i + 1 - k: s, k = s1, k + e - b if k <= i: s1, b, e = s.targets[T[k]] return s, k D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 29 Implementierung 1 2 3 def update(s, k, i, T, pos): old_r, n = None, len(T) endPoint, r = test_and_split(s, k, i, T) 4 5 6 7 8 9 10 while not endPoint: r.targets[T[i]] = (STNode(pos=pos), i, n) if old_r != None: old_r.s_link = r old_r, pos = r, pos + 1 s, k = canonize(s.s_link, k, i - 1, T) endPoint, r = test_and_split(s, k, i, T) 11 12 13 14 if old_r != None: old_r.s_link = s s, k = canonize(s, k, i, T) return s, k, pos D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 30 endPoint k r , 0) (−1 (−1, 0) $ a b , 0) babacbab$ 0 r − (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung c r 0 − D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 endPoint k r , 0) (−1 (−1, 0) $ a b , 0) babacbab$ 0 r − (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung c r 0 − D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 endPoint k r s1 e , 0) (−1 (−1, 0) $ a b , 0) babacbab$ 0 r − − (−1, 0) 1 def test_and_split(s, k, i, T): 2 if k == i: 3 return T[i] in s.targets, s 4 5 s1, b, e = s.targets[T[k]] 6 active = i - k + b 7 if T[i] == T[active]: 8 return True, s 9 10 r = STNode() 11 s.targets[T[b]] = (r, b, active) 12 r.targets[T[active]] = (s1, active, e) 13 return False, r T i s old r b t (−1 Implementierung c r 0 − − − D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 endPoint k r s1 e , 0) (−1 (−1, 0) $ a b , 0) babacbab$ 0 r − − (−1, 0) 1 def test_and_split(s, k, i, T): 2 if k == i: 3 return T[i] in s.targets, s 4 5 s1, b, e = s.targets[T[k]] 6 active = i - k + b 7 if T[i] == T[active]: 8 return True, s 9 10 r = STNode() 11 s.targets[T[b]] = (r, b, active) 12 r.targets[T[active]] = (s1, active, e) 13 return False, r T i s old r b t (−1 Implementierung c r 0 − − − D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 endPoint k r , 0) (−1 (−1, 0) $ a b , 0) babacbab$ 0 r − (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung c r false 0 r D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) a b , 0) c r 9) endPoint k r $ (0, babacbab$ 0 r r (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung b false 0 r 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) a b , 0) c r 9) endPoint k r $ (0, babacbab$ 0 r r (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung b false 0 r 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) a b , 0) c r 9) endPoint k r s1 e $ (0, babacbab$ −1 t r − (−1, 0) 1 def canonize(s, k, i, T): 2 if i < k: return s, k 3 4 s1, b, e = s.targets[T[k]] 5 while e - b <= i + 1 - k: 6 k += e - b 7 s = s1 8 if k <= i: 9 s1, b, e = s.targets[T[k]] 10 return s, k T i s old r b t (−1 Implementierung b false 0 r − − 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) a b , 0) c r 9) endPoint k r s1 e $ (0, babacbab$ −1 t r − (−1, 0) 1 def canonize(s, k, i, T): 2 if i < k: return s, k 3 4 s1, b, e = s.targets[T[k]] 5 while e - b <= i + 1 - k: 6 k += e - b 7 s = s1 8 if k <= i: 9 s1, b, e = s.targets[T[k]] 10 return s, k T i s old r b t (−1 Implementierung b false 0 r − − 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) a b , 0) c r 9) endPoint k r $ (0, babacbab$ 0 t r (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung b false 0 r 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) a b , 0) c r 9) endPoint k r s1 e $ (0, babacbab$ 0 t r − (−1, 0) 1 def test_and_split(s, k, i, T): 2 if k == i: 3 return T[i] in s.targets, s 4 5 s1, b, e = s.targets[T[k]] 6 active = i - k + b 7 if T[i] == T[active]: 8 return True, s 9 10 r = STNode() 11 s.targets[T[b]] = (r, b, active) 12 r.targets[T[active]] = (s1, active, e) 13 return False, r T i s old r b t (−1 Implementierung b false 0 r − − 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) a b , 0) c r 9) endPoint k r s1 e $ (0, babacbab$ 0 t r − (−1, 0) 1 def test_and_split(s, k, i, T): 2 if k == i: 3 return T[i] in s.targets, s 4 5 s1, b, e = s.targets[T[k]] 6 active = i - k + b 7 if T[i] == T[active]: 8 return True, s 9 10 r = STNode() 11 s.targets[T[b]] = (r, b, active) 12 r.targets[T[active]] = (s1, active, e) 13 return False, r T i s old r b t (−1 Implementierung b false 0 r − − 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) a b , 0) c r 9) endPoint k r $ (0, babacbab$ 0 t r (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung b true 0 t 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) a b , 0) c r 9) endPoint k r s1 e $ (0, babacbab$ 0 t r − (−1, 0) 1 def canonize(s, k, i, T): 2 if i < k: return s, k 3 4 s1, b, e = s.targets[T[k]] 5 while e - b <= i + 1 - k: 6 k += e - b 7 s = s1 8 if k <= i: 9 s1, b, e = s.targets[T[k]] 10 return s, k T i s old r b t (−1 Implementierung b true 0 t − − 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) a b , 0) c r 9) endPoint k r s1 e $ (0, babacbab$ 0 t r −1 (−1, 0) 1 def canonize(s, k, i, T): 2 if i < k: return s, k 3 4 s1, b, e = s.targets[T[k]] 5 while e - b <= i + 1 - k: 6 k += e - b 7 s = s1 8 if k <= i: 9 s1, b, e = s.targets[T[k]] 10 return s, k T i s old r b t (−1 Implementierung b true 0 t r 0 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) a b , 0) c r 9) endPoint k r s1 e $ (0, babacbab$ 0 r r −1 (−1, 0) 1 def canonize(s, k, i, T): 2 if i < k: return s, k 3 4 s1, b, e = s.targets[T[k]] 5 while e - b <= i + 1 - k: 6 k += e - b 7 s = s1 8 if k <= i: 9 s1, b, e = s.targets[T[k]] 10 return s, k T i s old r b t (−1 Implementierung b true 1 t r 0 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) a b , 0) c r 9) endPoint k r s1 e $ (0, babacbab$ 0 r r −1 (−1, 0) 1 def canonize(s, k, i, T): 2 if i < k: return s, k 3 4 s1, b, e = s.targets[T[k]] 5 while e - b <= i + 1 - k: 6 k += e - b 7 s = s1 8 if k <= i: 9 s1, b, e = s.targets[T[k]] 10 return s, k T i s old r b t (−1 Implementierung b true 1 t r 0 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) a b , 0) c r 9) endPoint k r $ (0, babacbab$ 0 r r (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung b true 1 t 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) a b , 0) c r 9) endPoint k r $ (0, babacbab$ 1 r − (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung b a 1 − 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) a b , 0) c r 9) endPoint k r $ (0, babacbab$ 1 r − (−1, 0) 1 def test_and_split(s, k, i, T): 2 if k == i: 3 return T[i] in s.targets, s 4 5 s1, b, e = s.targets[T[k]] 6 active = i - k + b 7 if T[i] == T[active]: 8 return True, s 9 10 r = STNode() 11 s.targets[T[b]] = (r, b, active) 12 r.targets[T[active]] = (s1, active, e) 13 return False, r T i s old r t (−1 Implementierung b a 1 − 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) a b , 0) c r 9) endPoint k r $ (0, babacbab$ 1 r − (−1, 0) 1 def test_and_split(s, k, i, T): 2 if k == i: 3 return T[i] in s.targets, s 4 5 s1, b, e = s.targets[T[k]] 6 active = i - k + b 7 if T[i] == T[active]: 8 return True, s 9 10 r = STNode() 11 s.targets[T[b]] = (r, b, active) 12 r.targets[T[active]] = (s1, active, e) 13 return False, r T i s old r t (−1 Implementierung b a 1 − 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) a b , 0) c r 9) endPoint k r $ (0, babacbab$ 1 r − (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung b a false 1 r 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) a b , 0) c (1 ,9 ) r a 9) endPoint k r $ (0, babacbab$ 1 r − (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung b a false 1 r 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) a b , 0) c (1 ,9 ) r a 9) endPoint k r $ (0, babacbab$ 1 r r (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung b a false 1 r 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) a b , 0) c (1 ,9 ) r a 9) endPoint k r s1 e $ (0, babacbab$ 0 t r − (−1, 0) 1 def canonize(s, k, i, T): 2 if i < k: return s, k 3 4 s1, b, e = s.targets[T[k]] 5 while e - b <= i + 1 - k: 6 k += e - b 7 s = s1 8 if k <= i: 9 s1, b, e = s.targets[T[k]] 10 return s, k T i s old r b t (−1 Implementierung b a false 1 r − − 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) a b , 0) c (1 ,9 ) r a 9) endPoint k r s1 e $ (0, babacbab$ 0 t r − (−1, 0) 1 def canonize(s, k, i, T): 2 if i < k: return s, k 3 4 s1, b, e = s.targets[T[k]] 5 while e - b <= i + 1 - k: 6 k += e - b 7 s = s1 8 if k <= i: 9 s1, b, e = s.targets[T[k]] 10 return s, k T i s old r b t (−1 Implementierung b a false 1 r − − 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) a b , 0) c (1 ,9 ) r a 9) endPoint k r $ (0, babacbab$ 1 t r (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung b a false 1 r 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) a b , 0) c (1 ,9 ) r a 9) endPoint k r s1 e $ (0, babacbab$ 1 t r − (−1, 0) 1 def test_and_split(s, k, i, T): 2 if k == i: 3 return T[i] in s.targets, s 4 5 s1, b, e = s.targets[T[k]] 6 active = i - k + b 7 if T[i] == T[active]: 8 return True, s 9 10 r = STNode() 11 s.targets[T[b]] = (r, b, active) 12 r.targets[T[active]] = (s1, active, e) 13 return False, r T i s old r b t (−1 Implementierung b a false 1 r − − 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) a b , 0) c (1 ,9 ) r a 9) endPoint k r s1 e $ (0, babacbab$ 1 t r − (−1, 0) 1 def test_and_split(s, k, i, T): 2 if k == i: 3 return T[i] in s.targets, s 4 5 s1, b, e = s.targets[T[k]] 6 active = i - k + b 7 if T[i] == T[active]: 8 return True, s 9 10 r = STNode() 11 s.targets[T[b]] = (r, b, active) 12 r.targets[T[active]] = (s1, active, e) 13 return False, r T i s old r b t (−1 Implementierung b a false 1 r − − 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) a b , 0) c (1 ,9 ) r a 9) endPoint k r $ (0, babacbab$ 1 t r (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung b a true 1 t 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) a b , 0) c (1 ,9 ) r a 9) endPoint k r s1 e $ (0, babacbab$ 1 t r − (−1, 0) 1 def canonize(s, k, i, T): 2 if i < k: return s, k 3 4 s1, b, e = s.targets[T[k]] 5 while e - b <= i + 1 - k: 6 k += e - b 7 s = s1 8 if k <= i: 9 s1, b, e = s.targets[T[k]] 10 return s, k T i s old r b t (−1 Implementierung b a true 1 t − − 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) a b , 0) c (1 ,9 ) r a 9) endPoint k r s1 e $ (0, babacbab$ 1 t r −1 (−1, 0) 1 def canonize(s, k, i, T): 2 if i < k: return s, k 3 4 s1, b, e = s.targets[T[k]] 5 while e - b <= i + 1 - k: 6 k += e - b 7 s = s1 8 if k <= i: 9 s1, b, e = s.targets[T[k]] 10 return s, k T i s old r b t (−1 Implementierung b a true 1 t r 0 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) a b , 0) c (1 ,9 ) r a 9) endPoint k r s1 e $ (0, babacbab$ 1 r r −1 (−1, 0) 1 def canonize(s, k, i, T): 2 if i < k: return s, k 3 4 s1, b, e = s.targets[T[k]] 5 while e - b <= i + 1 - k: 6 k += e - b 7 s = s1 8 if k <= i: 9 s1, b, e = s.targets[T[k]] 10 return s, k T i s old r b t (−1 Implementierung b a true 2 t r 0 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) a b , 0) c (1 ,9 ) r a 9) endPoint k r s1 e $ (0, babacbab$ 1 r r −1 (−1, 0) 1 def canonize(s, k, i, T): 2 if i < k: return s, k 3 4 s1, b, e = s.targets[T[k]] 5 while e - b <= i + 1 - k: 6 k += e - b 7 s = s1 8 if k <= i: 9 s1, b, e = s.targets[T[k]] 10 return s, k T i s old r b t (−1 Implementierung b a true 2 t r 0 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) a b , 0) c (1 ,9 ) r a 9) endPoint k r $ (0, babacbab$ 1 r r (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung b a true 2 t 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 (−1 $ a b , 0) (−1, 0) c (1 a b 9) ,9 ) r (0, 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos (−1, 0) , 0) t (−1 Implementierung b a b T i s old r babacbab$ 2 r − endPoint k r 2 − 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 (−1 $ a b , 0) (−1, 0) 1 def test_and_split(s, k, i, T): 2 if k == i: 3 return T[i] in s.targets, s 4 5 s1, b, e = s.targets[T[k]] 6 active = i - k + b 7 if T[i] == T[active]: 8 return True, s 9 10 r = STNode() 11 s.targets[T[b]] = (r, b, active) 12 r.targets[T[active]] = (s1, active, e) 13 return False, r (−1, 0) , 0) t (−1 Implementierung c (1 b 9) a (0, ,9 ) r b a b T i s old r b babacbab$ 2 r − − endPoint k r s1 e 2 − − − 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 (−1 $ a b , 0) (−1, 0) 1 def test_and_split(s, k, i, T): 2 if k == i: 3 return T[i] in s.targets, s 4 5 s1, b, e = s.targets[T[k]] 6 active = i - k + b 7 if T[i] == T[active]: 8 return True, s 9 10 r = STNode() 11 s.targets[T[b]] = (r, b, active) 12 r.targets[T[active]] = (s1, active, e) 13 return False, r (−1, 0) , 0) t (−1 Implementierung c (1 b 9) a (0, ,9 ) r b a b T i s old r b babacbab$ 2 r − − endPoint k r s1 e 2 − − − 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 (−1 $ a b , 0) (−1, 0) c (1 a b 9) ,9 ) r (0, 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos (−1, 0) , 0) t (−1 Implementierung b a b T i s old r babacbab$ 2 r − endPoint k r true 2 r 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 (−1 $ a b , 0) (−1, 0) 1 def canonize(s, k, i, T): 2 if i < k: return s, k 3 4 s1, b, e = s.targets[T[k]] 5 while e - b <= i + 1 - k: 6 k += e - b 7 s = s1 8 if k <= i: 9 s1, b, e = s.targets[T[k]] 10 return s, k (−1, 0) , 0) t (−1 Implementierung c (1 b 9) a (0, ,9 ) r b a b T i s old r b babacbab$ 2 r − − endPoint k r s1 e true 2 r − − 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 (−1 $ a b , 0) (−1, 0) 1 def canonize(s, k, i, T): 2 if i < k: return s, k 3 4 s1, b, e = s.targets[T[k]] 5 while e - b <= i + 1 - k: 6 k += e - b 7 s = s1 8 if k <= i: 9 s1, b, e = s.targets[T[k]] 10 return s, k (−1, 0) , 0) t (−1 Implementierung c (1 b 9) a (0, ,9 ) r b a b T i s old r b babacbab$ 2 r − 0 endPoint k r s1 e true 2 r 0 9 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 (−1 $ a b , 0) (−1, 0) 1 def canonize(s, k, i, T): 2 if i < k: return s, k 3 4 s1, b, e = s.targets[T[k]] 5 while e - b <= i + 1 - k: 6 k += e - b 7 s = s1 8 if k <= i: 9 s1, b, e = s.targets[T[k]] 10 return s, k (−1, 0) , 0) t (−1 Implementierung c (1 b 9) a (0, ,9 ) r b a b T i s old r b babacbab$ 2 r − 0 endPoint k r s1 e true 2 r 0 9 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 (−1 $ a b , 0) (−1, 0) c (1 a b 9) ,9 ) r (0, 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos (−1, 0) , 0) t (−1 Implementierung b a b T i s old r babacbab$ 2 r − endPoint k r 2 r 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 (−1 $ a b , 0) (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos (−1, 0) , 0) t (−1 Implementierung c (1 b 9) a (0, ,9 ) r b a b a T i s old r babacbab$ 3 r − endPoint k r 2 − a 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 (−1 $ a b , 0) (−1, 0) 1 def test_and_split(s, k, i, T): 2 if k == i: 3 return T[i] in s.targets, s 4 5 s1, b, e = s.targets[T[k]] 6 active = i - k + b 7 if T[i] == T[active]: 8 return True, s 9 10 r = STNode() 11 s.targets[T[b]] = (r, b, active) 12 r.targets[T[active]] = (s1, active, e) 13 return False, r (−1, 0) , 0) t (−1 Implementierung c (1 b 9) a (0, ,9 ) r b a b a T i s old r b babacbab$ 3 r − − endPoint k r s1 e 2 − − − a 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 (−1 $ a b , 0) (−1, 0) 1 def test_and_split(s, k, i, T): 2 if k == i: 3 return T[i] in s.targets, s 4 5 s1, b, e = s.targets[T[k]] 6 active = i - k + b 7 if T[i] == T[active]: 8 return True, s 9 10 r = STNode() 11 s.targets[T[b]] = (r, b, active) 12 r.targets[T[active]] = (s1, active, e) 13 return False, r (−1, 0) , 0) t (−1 Implementierung c (1 b 9) a (0, ,9 ) r b a b a T i s old r b babacbab$ 3 r − 0 endPoint k r s1 e 2 − 0 9 a 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 (−1 $ a b , 0) (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos (−1, 0) , 0) t (−1 Implementierung c (1 b 9) a (0, ,9 ) r b a b a T i s old r babacbab$ 3 r − endPoint k r true 2 r a 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 (−1 $ a b , 0) (−1, 0) 1 def canonize(s, k, i, T): 2 if i < k: return s, k 3 4 s1, b, e = s.targets[T[k]] 5 while e - b <= i + 1 - k: 6 k += e - b 7 s = s1 8 if k <= i: 9 s1, b, e = s.targets[T[k]] 10 return s, k (−1, 0) , 0) t (−1 Implementierung c (1 b 9) a (0, ,9 ) r b a b a T i s old r b babacbab$ 3 r − − endPoint k r s1 e true 2 r − − a 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 (−1 $ a b , 0) (−1, 0) 1 def canonize(s, k, i, T): 2 if i < k: return s, k 3 4 s1, b, e = s.targets[T[k]] 5 while e - b <= i + 1 - k: 6 k += e - b 7 s = s1 8 if k <= i: 9 s1, b, e = s.targets[T[k]] 10 return s, k (−1, 0) , 0) t (−1 Implementierung c (1 b 9) a (0, ,9 ) r b a b a T i s old r b babacbab$ 3 r − 0 endPoint k r s1 e true 2 r 0 9 a 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 (−1 $ a b , 0) (−1, 0) 1 def canonize(s, k, i, T): 2 if i < k: return s, k 3 4 s1, b, e = s.targets[T[k]] 5 while e - b <= i + 1 - k: 6 k += e - b 7 s = s1 8 if k <= i: 9 s1, b, e = s.targets[T[k]] 10 return s, k (−1, 0) , 0) t (−1 Implementierung c (1 b 9) a (0, ,9 ) r b a b a T i s old r b babacbab$ 3 r − 0 endPoint k r s1 e true 2 r 0 9 a 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 (−1 $ a b , 0) (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos (−1, 0) , 0) t (−1 Implementierung c (1 b 9) a (0, ,9 ) r b a b a T i s old r babacbab$ 3 r − endPoint k r true 2 r a 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) b c (1 ,9 ) r a b 9) 2 − a , 0) endPoint k r $ (0, babacbab$ 4 r − (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung b a b a c a c 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) a b , 0) c (1 ,9 ) r a b 2 − 0 9 9) endPoint k r s1 e $ (0, babacbab$ 4 r − 0 (−1, 0) 1 def test_and_split(s, k, i, T): 2 if k == i: 3 return T[i] in s.targets, s 4 5 s1, b, e = s.targets[T[k]] 6 active = i - k + b 7 if T[i] == T[active]: 8 return True, s 9 10 r = STNode() 11 s.targets[T[b]] = (r, b, active) 12 r.targets[T[active]] = (s1, active, e) 13 return False, r T i s old r b t (−1 Implementierung b a b a c a c 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 (−1 $ a b , 0) (−1, 0) 1 def test_and_split(s, k, i, T): 2 if k == i: 3 return T[i] in s.targets, s 4 5 s1, b, e = s.targets[T[k]] 6 active = i - k + b 7 if T[i] == T[active]: 8 return True, s 9 10 r = STNode() 11 s.targets[T[b]] = (r, b, active) 12 r.targets[T[active]] = (s1, active, e) 13 return False, r (−1, 0) , 0) t (−1 Implementierung c ) ,2 (1 (0 ,9 ) r b a a b 9) n0 T i s old r b babacbab$ 4 r − 0 endPoint k r s1 e 2 n0 0 9 (2 , a c b a c 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 (−1 $ a b , 0) (−1, 0) 1 def test_and_split(s, k, i, T): 2 if k == i: 3 return T[i] in s.targets, s 4 5 s1, b, e = s.targets[T[k]] 6 active = i - k + b 7 if T[i] == T[active]: 8 return True, s 9 10 r = STNode() 11 s.targets[T[b]] = (r, b, active) 12 r.targets[T[active]] = (s1, active, e) 13 return False, r (−1, 0) , 0) t (−1 Implementierung c ) ,2 (1 (0 ,9 ) r b a a b 9) n0 T i s old r b babacbab$ 4 r − 0 endPoint k r s1 e 2 n0 0 9 (2 , a c b a c 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) b c (1 ,9 ) r ) ,2 false 2 n0 a , 0) endPoint k r $ (0 babacbab$ 4 r − (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung b a a b 9) n0 (2 , a c b a c 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) b c (1 ,9 ) r ) ,2 b a a b n0 9) (4, (2 , a c 9) false 2 n0 a , 0) endPoint k r $ (0 babacbab$ 4 r − (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung c b a c 2 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) b c (1 ,9 ) r ) ,2 b a a b n0 9) (4, (2 , a c 9) false 2 n0 a , 0) endPoint k r $ (0 babacbab$ 4 r n0 (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung c b a c 2 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 (−1 $ a b , 0) (−1, 0) 1 def canonize(s, k, i, T): 2 if i < k: return s, k 3 4 s1, b, e = s.targets[T[k]] 5 while e - b <= i + 1 - k: 6 k += e - b 7 s = s1 8 if k <= i: 9 s1, b, e = s.targets[T[k]] 10 return s, k (−1, 0) , 0) t (−1 Implementierung c ) ,2 (1 (0 ,9 ) r b a a b n0 9) (4, babacbab$ 3 t n0 − endPoint k r s1 e false 2 n0 − − (2 , a c 9) T i s old r b c b a c 2 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 (−1 $ a b , 0) (−1, 0) 1 def canonize(s, k, i, T): 2 if i < k: return s, k 3 4 s1, b, e = s.targets[T[k]] 5 while e - b <= i + 1 - k: 6 k += e - b 7 s = s1 8 if k <= i: 9 s1, b, e = s.targets[T[k]] 10 return s, k (−1, 0) , 0) t (−1 Implementierung c ) ,2 (1 (0 ,9 ) r b a a b n0 9) (4, babacbab$ 3 t n0 −1 endPoint k r s1 e false 2 n0 r 0 (2 , a c 9) T i s old r b c b a c 2 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 (−1 $ a b , 0) (−1, 0) 1 def canonize(s, k, i, T): 2 if i < k: return s, k 3 4 s1, b, e = s.targets[T[k]] 5 while e - b <= i + 1 - k: 6 k += e - b 7 s = s1 8 if k <= i: 9 s1, b, e = s.targets[T[k]] 10 return s, k (−1, 0) , 0) t (−1 Implementierung c ) ,2 (1 (0 ,9 ) r b a a b n0 9) (4, babacbab$ 3 r n0 −1 endPoint k r s1 e false 3 n0 r 0 (2 , a c 9) T i s old r b c b a c 2 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 (−1 $ a b , 0) (−1, 0) 1 def canonize(s, k, i, T): 2 if i < k: return s, k 3 4 s1, b, e = s.targets[T[k]] 5 while e - b <= i + 1 - k: 6 k += e - b 7 s = s1 8 if k <= i: 9 s1, b, e = s.targets[T[k]] 10 return s, k (−1, 0) , 0) t (−1 Implementierung c ) ,2 (1 (0 ,9 ) r b a a b n0 9) (4, babacbab$ 3 r n0 1 endPoint k r s1 e false 3 n0 1 9 (2 , a c 9) T i s old r b c b a c 2 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 (−1 $ a b , 0) (−1, 0) 1 def canonize(s, k, i, T): 2 if i < k: return s, k 3 4 s1, b, e = s.targets[T[k]] 5 while e - b <= i + 1 - k: 6 k += e - b 7 s = s1 8 if k <= i: 9 s1, b, e = s.targets[T[k]] 10 return s, k (−1, 0) , 0) t (−1 Implementierung c ) ,2 (1 (0 ,9 ) r b a a b n0 9) (4, babacbab$ 3 r n0 1 endPoint k r s1 e false 3 n0 1 9 (2 , a c 9) T i s old r b c b a c 2 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) b c (1 ,9 ) r ) ,2 b a a b n0 9) (4, (2 , a c 9) false 3 n0 a , 0) endPoint k r $ (0 babacbab$ 4 r n0 (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung c b a c 2 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 (−1 $ a b , 0) (−1, 0) 1 def test_and_split(s, k, i, T): 2 if k == i: 3 return T[i] in s.targets, s 4 5 s1, b, e = s.targets[T[k]] 6 active = i - k + b 7 if T[i] == T[active]: 8 return True, s 9 10 r = STNode() 11 s.targets[T[b]] = (r, b, active) 12 r.targets[T[active]] = (s1, active, e) 13 return False, r (−1, 0) , 0) t (−1 Implementierung c ) ,2 (1 (0 ,9 ) r b a a b n0 9) (4, babacbab$ 4 r n0 − endPoint k r s1 e false 3 n0 − − (2 , a c 9) T i s old r b c b a c 2 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 (−1 $ a b , 0) (−1, 0) 1 def test_and_split(s, k, i, T): 2 if k == i: 3 return T[i] in s.targets, s 4 5 s1, b, e = s.targets[T[k]] 6 active = i - k + b 7 if T[i] == T[active]: 8 return True, s 9 10 r = STNode() 11 s.targets[T[b]] = (r, b, active) 12 r.targets[T[active]] = (s1, active, e) 13 return False, r (−1, 0) , 0) t (−1 Implementierung c ) ,2 (1 (0 ,9 ) r b a a b n0 9) (4, babacbab$ 4 r n0 1 endPoint k r s1 e false 3 n0 1 9 (2 , a c 9) T i s old r b c b a c 2 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 (−1 $ a b r ) ,2 ,2 c (0 ) (1 , 0) (−1, 0) 1 def test_and_split(s, k, i, T): 2 if k == i: 3 return T[i] in s.targets, s 4 5 s1, b, e = s.targets[T[k]] 6 active = i - k + b 7 if T[i] == T[active]: 8 return True, s 9 10 r = STNode() 11 s.targets[T[b]] = (r, b, active) 12 r.targets[T[active]] = (s1, active, e) 13 return False, r (−1, 0) , 0) t (−1 Implementierung b a n1 (2, 9) a b n0 endPoint k r s1 e false 3 n1 1 9 a c 9) babacbab$ 4 r n0 1 (2 , 9) (4, T i s old r b c b a c 2 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 (−1 $ a b r ) ,2 ,2 c (0 ) (1 , 0) (−1, 0) 1 def test_and_split(s, k, i, T): 2 if k == i: 3 return T[i] in s.targets, s 4 5 s1, b, e = s.targets[T[k]] 6 active = i - k + b 7 if T[i] == T[active]: 8 return True, s 9 10 r = STNode() 11 s.targets[T[b]] = (r, b, active) 12 r.targets[T[active]] = (s1, active, e) 13 return False, r (−1, 0) , 0) t (−1 Implementierung b a n1 (2, 9) a b n0 endPoint k r s1 e false 3 n1 1 9 a c 9) babacbab$ 4 r n0 1 (2 , 9) (4, T i s old r b c b a c 2 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) b ) (1 ,2 c r ) ,2 b a n1 (2, 9) a b n0 a c 9) (2 , 9) (4, false 3 n1 a , 0) endPoint k r $ (0 babacbab$ 4 r n0 (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung c b a c 2 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) ) ,2 c r ) ,2 (1 , 0) b (0 b a (2, 9) n1 b a c n0 a c 9) (2 , 9) (4, false 3 n1 a 9) endPoint k r $ (4, babacbab$ 4 r n0 (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung c b 3 a c 2 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) ) ,2 c r ) ,2 (1 , 0) b (0 b a (2, 9) n1 b a c n0 a c 9) (2 , 9) (4, false 3 n1 a 9) endPoint k r $ (4, babacbab$ 4 r n0 (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung c b 3 a c 2 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) ) ,2 c r ) ,2 (1 , 0) b (0 b a (2, 9) n1 b a c n0 a c 9) (2 , 9) (4, false 3 n1 a 9) endPoint k r $ (4, babacbab$ 4 r n1 (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung c b 3 a c 2 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 (−1 $ a b r ) ,2 ,2 c (0 ) (1 , 0) (−1, 0) 1 def canonize(s, k, i, T): 2 if i < k: return s, k 3 4 s1, b, e = s.targets[T[k]] 5 while e - b <= i + 1 - k: 6 k += e - b 7 s = s1 8 if k <= i: 9 s1, b, e = s.targets[T[k]] 10 return s, k (−1, 0) , 0) t (−1 Implementierung b a (2, a 9) b (4, 9) n1 c n0 endPoint k r s1 e false 3 n1 − − a c 9) babacbab$ 3 t n1 − (2 , 9) (4, T i s old r b c b 3 a c 2 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 (−1 $ a b r ) ,2 ,2 c (0 ) (1 , 0) (−1, 0) 1 def canonize(s, k, i, T): 2 if i < k: return s, k 3 4 s1, b, e = s.targets[T[k]] 5 while e - b <= i + 1 - k: 6 k += e - b 7 s = s1 8 if k <= i: 9 s1, b, e = s.targets[T[k]] 10 return s, k (−1, 0) , 0) t (−1 Implementierung b a (2, a 9) b (4, 9) n1 c n0 endPoint k r s1 e false 3 n1 r 0 a c 9) babacbab$ 3 t n1 −1 (2 , 9) (4, T i s old r b c b 3 a c 2 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 (−1 $ a b r ) ,2 ,2 c (0 ) (1 , 0) (−1, 0) 1 def canonize(s, k, i, T): 2 if i < k: return s, k 3 4 s1, b, e = s.targets[T[k]] 5 while e - b <= i + 1 - k: 6 k += e - b 7 s = s1 8 if k <= i: 9 s1, b, e = s.targets[T[k]] 10 return s, k (−1, 0) , 0) t (−1 Implementierung b a (2, a 9) b (4, 9) n1 c n0 endPoint k r s1 e false 4 n1 r 0 a c 9) babacbab$ 3 r n1 −1 (2 , 9) (4, T i s old r b c b 3 a c 2 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 (−1 $ a b r ) ,2 ,2 c (0 ) (1 , 0) (−1, 0) 1 def canonize(s, k, i, T): 2 if i < k: return s, k 3 4 s1, b, e = s.targets[T[k]] 5 while e - b <= i + 1 - k: 6 k += e - b 7 s = s1 8 if k <= i: 9 s1, b, e = s.targets[T[k]] 10 return s, k (−1, 0) , 0) t (−1 Implementierung b a (2, a 9) b (4, 9) n1 c n0 endPoint k r s1 e false 4 n1 r 0 a c 9) babacbab$ 3 r n1 −1 (2 , 9) (4, T i s old r b c b 3 a c 2 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) ) ,2 c r ) ,2 (1 , 0) b (0 b a (2, 9) n1 b a c n0 a c 9) (2 , 9) (4, false 4 n1 a 9) endPoint k r $ (4, babacbab$ 4 r n1 (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung c b 3 a c 2 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) ) ,2 c r ) ,2 (1 , 0) b (0 b a (2, 9) n1 b a c n0 a c 9) (2 , 9) (4, false 4 r a 9) endPoint k r $ (4, babacbab$ 4 r n2 (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung c b 3 a c 2 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) ) ,2 r c (4, 9) ) ,2 (1 , 0) b (0 c b a (2, 9) n1 b a c 4 n0 a c 9) (2 , 9) (4, false 4 r a 9) endPoint k r $ (4, babacbab$ 4 r n1 (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung c b 3 a c 2 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) ) ,2 r c (4, 9) ) ,2 (1 , 0) b (0 c b a (2, 9) n1 b a c 4 n0 a c 9) (2 , 9) (4, false 4 r a 9) endPoint k r $ (4, babacbab$ 4 r n1 (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung c b 3 a c 2 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) ) ,2 r c (4, 9) ) ,2 (1 , 0) b (0 c b a (2, 9) n1 b a c 4 n0 a c 9) (2 , 9) (4, false 4 r a 9) endPoint k r $ (4, babacbab$ 4 t r (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung c b 3 a c 2 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) ) ,2 r c (4, 9) ) ,2 (1 , 0) b (0 c b a (2, 9) n1 b a c 4 n0 a c 9) (2 , 9) (4, true 4 t a 9) endPoint k r $ (4, babacbab$ 4 t r (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung c b 3 a c 2 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) ) ,2 r c (4, 9) ) ,2 (1 , 0) b (0 c b a (2, 9) n1 b a c 4 n0 a c 9) (2 , 9) (4, true 5 t a 9) endPoint k r $ (4, babacbab$ 4 r r (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung c b 3 a c 2 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) ) ,2 r c (4, 9) ) ,2 (1 , 0) b (0 c b a (2, 9) n1 b a c 4 n0 a c 9) (2 , 9) (4, true 5 t a 9) endPoint k r $ (4, babacbab$ 4 r r (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung c b 3 a c 2 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) ) ,2 r c (4, 9) ) ,2 (1 , 0) b (0 c b b a (2, 9) n1 b a c b 9) (2 , 9) a c b 4 n0 (4, 5 − a 9) endPoint k r $ (4, babacbab$ 5 r − (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung c b b 3 a c b 2 1 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) ) ,2 r c (4, 9) ) ,2 (1 , 0) b (0 c b a b a (2, 9) n1 b a c b a 9) (2 , 9) a c b a c b a b 3 1 4 n0 (4, 7 − a 9) endPoint k r $ (4, babacbab$ 6 n0 − (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung a c b a 2 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) ) ,2 r c (4, 9) ) ,2 (1 , 0) b (0 c b a b a b a a c b a b 3 1 4 n0 9) c b a b 9) b (2 , (2, 9) n1 (4, 7 − a 9) endPoint k r $ (4, babacbab$ 7 n0 − (−1, 0) 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung c b a b b a c b a b 2 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) r ) ,2 c (4, 9) c ) ,2 (1 , 0) b (0 b b a a b $ a ( 5 9) b n2 $ 1 4 n0 (4, a c b a b $ ) 3 2, 9) c b a b $ 3 9) b (8, (2, 9) n1 (3 , 7 − (−1, 0) endPoint k r a 9) babacbab$ 8 n1 − $ (4, 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung a c b a b $ c b a b $ 2 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) , 0) (−1, 0) r (4, ) ,2 (2 b $ a 4 n0 ( 9) $ 5 9) b n2 9) 1 ) 3 2, (3 , a c b a b $ c b a b $ 3 (8, 9) a (4, (8, b n1 b n3 6 c b a ) ,3 9) (0 ,2 c 9) 7 − b (4, endPoint k r a ) $ babacbab$ 8 r − $ (1 ) (3, 9 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung a c b a b $ c b a b $ 2 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 , 0) (−1 (−1, 0) (0 9) (8, a b $ a $ 4 c 7 ) n0 3 , b (2 a b b c n2 $ b a 3 b a $ $ c b 2 5 a b $ (4, 9) 9) 9) 1 (3 , a c b a b $ (8, 9) b ) ,2 (8, c (1 6 9) ) ,1 (2 n4 n1 b n3 (4, b a ) ,3 , 0) (−1, 0) r ,2 c 9) 8 − b (4, endPoint k r a ) $ babacbab$ 8 r − $ (1 ) (3, 9 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 c , 0) (−1 (−1, 0) b n4 9) (8, a b $ ) ,2 n1 b (1 a $ 4 c 7 ) n0 3 , b (2 a b b c n2 $ b a 3 b a $ $ c b 2 5 a b $ (4, 9) 9) 9) 1 (3 , a c b a b $ (8, 9) 9) ) ,1 (8, (4, (0 (2 ) ,3 b n3 6 r , 0) (−1, 0) ) (8, 9 ) ,2 (1 c 9) 9 − b (4, endPoint k r a a 8 $ babacbab$ 8 r − $ $ ) (3, 9 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 c , 0) (−1 (−1, 0) b n4 9) (8, a b $ ) ,2 n1 b (1 a $ 4 c 7 ) n0 3 , b (2 a b b c n2 $ b a 3 b a $ $ c b 2 5 a b $ (4, 9) 9) 9) 1 (3 , a c b a b $ (8, 9) 9) ) ,1 (8, (4, (0 (2 ) ,3 b n3 6 r , 0) (−1, 0) ) (8, 9 ) ,2 (1 c 9) 9 − b (4, endPoint k r a a 8 $ babacbab$ 8 − − $ $ ) (3, 9 1 def update(s, k, i, T, pos): 2 old_r, n = None, len(T) 3 endPoint, r = test_and_split(s, k, i, T) 4 5 while not endPoint: 6 r.targets[T[i]] = (STNode(pos=pos), i, n) 7 if old_r != None: old_r.s_link = r 8 old_r, pos = r, pos + 1 9 s, k = canonize(s.s_link, k, i - 1, T) 10 endPoint, r = test_and_split(s, k, i, T) 11 12 if old_r != None: old_r.s_link = s 13 s, k = canonize(s, k, i, T) 14 return s, k, pos T i s old r t (−1 Implementierung 0 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 31 Laufzeitanalyse While-Schleife in canonize: Zu beginn ist k = 0. Die While-Bedingung lautet e − b ≤ i + 1 − k. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 32 Laufzeitanalyse While-Schleife in canonize: Zu beginn ist k = 0. Die While-Bedingung lautet e − b ≤ i + 1 − k. Für alle Intervalle gilt: e − b ≥ 1. Pro Betreten der While-Schleife wird k um mind. 1 erhöht. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 32 Laufzeitanalyse While-Schleife in canonize: Zu beginn ist k = 0. Die While-Bedingung lautet e − b ≤ i + 1 − k. Für alle Intervalle gilt: e − b ≥ 1. Pro Betreten der While-Schleife wird k um mind. 1 erhöht. Sobald i < k ist, bricht die While-Schleife ab. Insgesamt kann k max. n mal um 1 erhöht werden. Somit kann die Schleife auch nur O(n) mal betreten werden. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 32 Laufzeitanalyse While-schleife in update: Pro update-Aufruf wird die Stringtiefe der aktuellen Position um 1 erhöht. Pro While-Durchlauf wird die Stringtiefe durch das Beschreiten der Suffixlinks um 1 verringert. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 33 Laufzeitanalyse While-schleife in update: Pro update-Aufruf wird die Stringtiefe der aktuellen Position um 1 erhöht. Pro While-Durchlauf wird die Stringtiefe durch das Beschreiten der Suffixlinks um 1 verringert. Die Stringtiefe kann nicht unter 0 sinken. Die Stringtiefe wird genau n mal erhöht, also kann sie auch nur n mal um 1 verringert werden. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 33 Laufzeitanalyse While-schleife in update: Pro update-Aufruf wird die Stringtiefe der aktuellen Position um 1 erhöht. Pro While-Durchlauf wird die Stringtiefe durch das Beschreiten der Suffixlinks um 1 verringert. Die Stringtiefe kann nicht unter 0 sinken. Die Stringtiefe wird genau n mal erhöht, also kann sie auch nur n mal um 1 verringert werden. Der Ukkonen-Algorithmus konstruiert einen Suffix-Tree also in O(n). D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 33 Suffix-Array aus Suffix-Tree erstellen Ein Suffix-Array pos gibt die Startpositionen der Suffixe in lexikographischer Reihenfolge an. Für T = babacbab$ : pos = [8, 6, 1, 3, 7, 5, 0, 2, 4]. Das Suffix-Array enthält dieselben Informationen wie die Blätter eines Suffix-Trees. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 34 Suffix-Array aus Suffix-Tree erstellen Ein Suffix-Array pos gibt die Startpositionen der Suffixe in lexikographischer Reihenfolge an. Für T = babacbab$ : pos = [8, 6, 1, 3, 7, 5, 0, 2, 4]. Das Suffix-Array enthält dieselben Informationen wie die Blätter eines Suffix-Trees. 1 2 3 4 5 6 7 def get_suffixarray(node, pos = None): if pos == None: pos = [] if len(node.targets): for c in sorted(node.targets): get_suffixarray(node.targets[c][0], pos) else: pos += [node.pos] return pos D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 34 Mustersuche im Suffix-Tree Fragestellung: kommt P in T vor? Für die Mustersuche ist wieder das Tupel (s, k, i) erforderlich: Die Mustersuche startet mit (root, 0, 0): D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 35 Mustersuche im Suffix-Tree Fragestellung: kommt P in T vor? Für die Mustersuche ist wieder das Tupel (s, k, i) erforderlich: Die Mustersuche startet mit (root, 0, 0): Drei Möglichkeiten: Wenn i = |P|: True zurückgeben. Wenn i < |P| und k == i: Prüfen ob s eine ausgehende Kante mit P[i] hat. Wenn i < |P| und k < i: sei s1, b, e = s.targets[P[k]], prüfen ob P[i] = T [i − k + b]. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 35 Mustersuche im Suffix-Tree Fragestellung: kommt P in T vor? Für die Mustersuche ist wieder das Tupel (s, k, i) erforderlich: Die Mustersuche startet mit (root, 0, 0): Drei Möglichkeiten: Wenn i = |P|: True zurückgeben. Wenn i < |P| und k == i: Prüfen ob s eine ausgehende Kante mit P[i] hat. Wenn i < |P| und k < i: sei s1, b, e = s.targets[P[k]], prüfen ob P[i] = T [i − k + b]. Wenn Fall 2 oder 3 nicht zutrifft: False zurückgeben. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 35 Mustersuche im Suffix-Tree Fragestellung: kommt P in T vor? Für die Mustersuche ist wieder das Tupel (s, k, i) erforderlich: Die Mustersuche startet mit (root, 0, 0): Drei Möglichkeiten: Wenn i = |P|: True zurückgeben. Wenn i < |P| und k == i: Prüfen ob s eine ausgehende Kante mit P[i] hat. Wenn i < |P| und k < i: sei s1, b, e = s.targets[P[k]], prüfen ob P[i] = T [i − k + b]. Wenn Fall 2 oder 3 nicht zutrifft: False zurückgeben. Überprüfen ob nach gelesenem Zeichen im Tree ein Knoten kommt und ggf. betreten: wenn e − b ≤ i + 1 − k : s = s1, k = k + e − b. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 35 Mustersuche im Suffix-Tree Fragestellung: kommt P in T vor? Für die Mustersuche ist wieder das Tupel (s, k, i) erforderlich: Die Mustersuche startet mit (root, 0, 0): Drei Möglichkeiten: Wenn i = |P|: True zurückgeben. Wenn i < |P| und k == i: Prüfen ob s eine ausgehende Kante mit P[i] hat. Wenn i < |P| und k < i: sei s1, b, e = s.targets[P[k]], prüfen ob P[i] = T [i − k + b]. Wenn Fall 2 oder 3 nicht zutrifft: False zurückgeben. Überprüfen ob nach gelesenem Zeichen im Tree ein Knoten kommt und ggf. betreten: wenn e − b ≤ i + 1 − k : s = s1, k = k + e − b. Suche mit i + 1 wiederholen. Laufzeit insgesamt: O(|P|). D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 35 Mustersuche im Suffix-Tree Fragestellung: wo kommt P in T vor? Patternsuche leicht modifizieren. Wenn i = |P|: überprüfen, ob die aktuelle Position an einem Knoten ist, wenn k < i : s = s.targets[P[k]][0]. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 36 Mustersuche im Suffix-Tree Fragestellung: wo kommt P in T vor? Patternsuche leicht modifizieren. Wenn i = |P|: überprüfen, ob die aktuelle Position an einem Knoten ist, wenn k < i : s = s.targets[P[k]][0]. Vom Knoten s alle Blätter besuchen und deren pos-Wert in einer Liste zurückgeben. Bei Mismatch leere Liste zurück geben. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 36 Mustersuche im Suffix-Tree Fragestellung: wo kommt P in T vor? Patternsuche leicht modifizieren. Wenn i = |P|: überprüfen, ob die aktuelle Position an einem Knoten ist, wenn k < i : s = s.targets[P[k]][0]. Vom Knoten s alle Blätter besuchen und deren pos-Wert in einer Liste zurückgeben. Bei Mismatch leere Liste zurück geben. Sei z die Anzahl der Blätter, dann ist die Gesamtlaufzeit O(|P| + z). D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 36 Mustersuche im Suffix-Tree 1 2 3 def search_pattern(s, T, P, k = 0): for i, c in enumerate(P): if k == i and c not in s.targets: return [] 4 5 6 s1, b, e = s.targets[P[k]] if k < i and c != T[i - k + b]: return [] 7 8 9 if e - b <= i + 1 - k: s, k = s1, k + e - b 10 11 12 if k < len(P): s = s.targets[P[k]][0] return get_suffixarray(s) D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 37 Längster wiederholter Teilstring Sei s ∗ := arg maxs∈nodes(S) {strdep(s) | s ist innerer Knoten}, dann ist der längste wiederholte Teilstring (LRS) t = str(s ∗ ). D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 38 Längster wiederholter Teilstring Sei s ∗ := arg maxs∈nodes(S) {strdep(s) | s ist innerer Knoten}, dann ist der längste wiederholte Teilstring (LRS) t = str(s ∗ ). Beispiel: T = ananas$ s$ ] :7 ] [1:3 a [0 :1] [6 na $ :7 ] 5 [1: 3 na 4 7] [5: s$ nas$ [3:7] s$ [5:7] ] 6 [5 1 3 7] [5: s$ nas$ [3:7] 2 0 LRS hier: ana D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 38 Kürzester eindeutiger Teilstring Sei s ∗ := argmins∈nodes(S) {strdep(s) | s hat Blattkante e, |e| > 1}, dann ist der kürzeste eindeutige Teilstring (SUS) t = str(s ∗ ) ◦ e[0]. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 39 Kürzester eindeutiger Teilstring Sei s ∗ := argmins∈nodes(S) {strdep(s) | s hat Blattkante e, |e| > 1}, dann ist der kürzeste eindeutige Teilstring (SUS) t = str(s ∗ ) ◦ e[0]. Beispiel: T = baabbaabb$ $ b a 9 a $ b b b b $ 5 a a b b $ $ 6 a a b b $ 2 1 b a a b b 8 $ 4 $ 7 a a b b $ SUS hier: bba D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes a a b b $ 3 0 39 Längster gemeinsamer Teilstring Gegeben seien die Texte T1 und T2 . Gesucht ist der längste gemeinsame Teilstring von T1 und T2 . D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 40 Längster gemeinsamer Teilstring Gegeben seien die Texte T1 und T2 . Gesucht ist der längste gemeinsame Teilstring von T1 und T2 . Sei dementsprechend der verallgemeinerte Text T = T1 $1 T2 $2 mit $1 < $2 . Suffix-Tree auf T aufbauen D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 40 Längster gemeinsamer Teilstring Gegeben seien die Texte T1 und T2 . Gesucht ist der längste gemeinsame Teilstring von T1 und T2 . Sei dementsprechend der verallgemeinerte Text T = T1 $1 T2 $2 mit $1 < $2 . Suffix-Tree auf T aufbauen Alle Blätter mit pos-Wert < |T1 $1 | mit 1 beschriften. Alle restlichen Blätter mit 2 beschriften. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 40 Längster gemeinsamer Teilstring Innere Knoten bitweise mit den Labels seiner Kinder verodern. Sei s ∗ := arg maxs∈nodes(S) {strdep(s) | s ist inn. Knoten, label(s) = 3}, dann ist der längste gemeinsame Teilstring (LCS) t = str(s ∗ ). D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 41 Längster gemeinsamer Teilstring Innere Knoten bitweise mit den Labels seiner Kinder verodern. Sei s ∗ := arg maxs∈nodes(S) {strdep(s) | s ist inn. Knoten, label(s) = 3}, dann ist der längste gemeinsame Teilstring (LCS) t = str(s ∗ ). Beispiel: T = aaab$1 abcc$2 · · · $1 $2 3 a 1 $1 LCS hier: ab · · · · 01 $1 b · $1 b a · b · · $1 3 a · · · 92 · 41 11 21 c b 3 c 31 c $2 3 2 c c $2 c $2 82 $2 72 62 52 D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 41 Zusammenfassung Der Ukkonen-Algorithmus erstellt online einen Suffix-Tree in O(n). Verschiedene Fragestellungen können mit dem Suffix-Tree beantwortet werden: Kommt P in T vor? Wenn ja, wo? Welcher ist der längste wiederholte Teilstring im Text T ? Welcher ist der kürzeste eindeutige Teilstring im Text T ? Welcher ist der längste gemeinsame Teilstring der beiden Texte T1 und T2 ? Leider hohe Konstante beim Speicherverbrauch, bei guten Implementierungen ca. 20 Bytes pro Zeichen. D. Kopczynski | Algorithmen auf Sequenzen | SoSe 2015 | Volltext-Indizes 42