Benutzer-Werkzeuge

Webseiten-Werkzeuge


paper:least_common_ancestor:section06

Effiziente Berechnung vom letzten gemeinsamen Vorfahren und Anwendungen

Antonia Kresse
Verfasser:Antonia Kresse
Erstgutachter:Dr. Frank Hoffmann
Zweitgutachter: Prof. Dr. Helmut Alt
Freie Universität Berlin

6 Zwei Anwendungsbeispiele und eine Verallgemeinerung

In den Unterkapiteln 6.1 und 6.2 werden zwei bioinformatische Anwendungenerklärt, welche sich an [GUS97] orientieren. Anschließend wird in 6.3 das LCA-Problem auf gerichtete azyklische Graphen verallgemeinert und Lösungsansätze sowie weitere Anwendungen skizziert. Die in 6.3 vorgestellten Verfahren basieren auf [BPSS01].

6.1 Maximale Palindrome

Dieses Unterkapitel beschreibt das Vorgehen zur Ermittlung maximaler Palindrome in einer Zeichenkette T.

Sei Σ ein endliches Alphabet und T = σ0σ1...σn-1 ∈ Σ+ ein Eingabestring. SeiS = T[i,j] = σiσi+1...σj ein Teilstring mit der Länge |S| = j - i + 1. Der umgekehrte Teilstring Srev ist folglich σjσj-1...σi.

Eine Zeichenkette w ∈ Σ+ ist ein Präfix eines Strings T, wenn T = wy für einen String y ∈ Σ* gilt. Analog ist eine Zeichenkette w ein Suffix eines Strings T, wenn T = yw für einen String y ∈ Σ* gilt [CLRS01]. Den Suffix T[i,…,n] eines Strings T bezeichnet man als i - ten Suffix von T.

6.1.1 Das LCE-Problem

Als wichtige Teilfrage zur Lösung einiger Probleme für Zeichenketten hat sich die Bestimmung von längsten gemeinsamen Ausdehnungen (LCE - longest commonextension) erwiesen.

Definition 8 (LCE).
Gegeben seien zwei Zeichenketten S1 und S2 ∈ Σ+ mit Indexpositionen i für S1und j für S2. LCE(i,j) bezeichnet die Länge des längsten gemeinsamen Präfixes des i-ten Suffix von S1 und des j-ten Suffix von S2.

Das LCE-Problem kann wie folgt definiert werden: Zwei Zeichenketten S1 und S2 ∈ Σ+ sollen so effizient vorverarbeitet werden, dass LCE(i,j)-Anfragen möglichst schnell beantwortet werden können.

Hinweis: Zu beachten ist hier, dass nicht eine einzelne LCE-Anfrage beantwortet werden soll, sondern viele und diese möglichst effektiv. Eine einzelne LCE-Anfrage könnte durch den trivialen Vergleich, Zeichen für Zeichen, beantwortet werden.

Gegeben seien folgende zwei Strings:

PIC

Der LCE(0,2) = 3, da die Zeichen cab übereinstimmen, jedoch nicht die darauf folgenden Zeichen, was in S1 b und in S2 dem Ende des Strings entspricht. Weitere Beispiele sind LCE(0,0) = 0, da sich keine gleichen Zeichen an den gegebenen Indexpositionen befinden und der LCE(1,1) = 1.

Der Schlüssel zur effizienten Lösung des LCE-Problems liegt in der Verwendung eines Suffixbaumes als Datenstruktur.

Definition 9 (Suffixbaum).
Ein Suffixbaum B ist eine Datenstruktur für einen String T = σ0σ1...σn-1 ∈ Σn. Bspeichert alle Suffixe des Strings T ab. Um diese eindeutig identifizieren zu können, fügen wir $ als Sonderzeichen hinter das letzte Zeichen von T an die Position n ein. Für einen String der Länge n hat ein Suffixbaum folgende Eigenschaften:
  1. B ist ein Baum mit n + 1 Blättern, welche die Markierungen 0, …, n haben.
  2. Jeder innere Knoten, außer der Wurzel, hat mindestens zwei Kinder.
  3. Jede Kante E ist mit einem nichtleeren Teilstring von T beschriftet.
  4. Alle Kantenmarkierungen, die von einem Knoten ausgehen, beginnen mit unterschiedlichen Zeichen.
  5. Das Blatt mit Markierung i entspricht dem i-ten Suffix. Genauer der Konkatenation der Kantenlabel von der Wurzel bis zum Blatt mit der Markierung i, welche dem Teilstring T[i,…,n entspricht].

Hinweis: Die Kantenmarkierungen sind durch Verweise auf die Indexpositionen i und j für den Teilstring T[i,j] realisiert.

Dies lässt sich auf den Fall mehrerer Eingabestrings verallgemeinern. In diesem Fall werden die beiden Strings S1 und S2 in einen generalisierten Suffixbaum überführt. Ein generalisierter Suffixbaum ist ein gemeinsamer Suffixbaum von mehreren unterschiedlichen Zeichenketten. In den Blättern stehen Tupel der Form (Sx,i), die angeben zu welchem String(Sx) der Suffix gehört und an welcher Stelle i er beginnt.

Satz 3.
Ein generalisierter Suffixbaum für zwei Strings S1 und S2 kann in O(|S1| + |S2|) konstruiert werden. [GUS97]

Zusätzlich werden in jedem Knoten v des Suffixbaumes die Länge der Konkatenation der Kantenlabel von der Wurzel bis v gespeichert. Der gemeinsam generalisierte Suffixbaum wird in der Vorbereitungsphase des Algorithmus erstellt.

Die Grafik 6.1 zeigt ein Beispiel eines gemeinsamen Suffixbaumes für die StringsS1 = cabbabc und S2 = aacab.

PIC Abbildung 6.1: Generalisierter Suffixbaum für S1 und S2

Nach der linearen Vorverarbeitung des Suffixbaumes für LCA-Anfragen können LCA-Anfragen mit allen Kombinationen aus den Indizes von S1 und S2 gestellt werden.

Beobachtung: Der Knoten u, welcher der letzte gemeinsame Vorfahre der beiden Indizes i und j ist, hat den Wert der längsten gemeinsamen Ausdehnung als Markierung gespeichert.

Satz 4.
LCE(i,j)-Anfragen für zwei Strings S1, S2 können nach linearer Vorverarbeitung in konstanter Zeit beantwortet werden.

Betrachten wir den Suffix aus S1 beginnend bei i = 4 und den Suffix aus S2 beginnend bei j = 3, so speichert der letzte gemeinsame Vorfahre der beiden Blätter den Wert 2, was bedeutet, dass die längste gemeinsame Ausdehnung die Länge 2 hat.

6.1.2 Ermittlung maximaler Palindrome

Definition 10 (Palindrom).
Eine Zeichenkette S ∈ Σ+ ist ein Palindrom, wenn S = Srev gilt.
Definition 11 (maximales Palindrom).
Ein Teilstring S=T[i,j] heißt maximales Palindrom in T, falls T[i,j] ein Palindrom und T[i-1, j+1] kein Palindrom ist.

Das Palindrom S = T[i,j] hat die Länge j - i + 1 und den Radius r = ⌊j-2i+1-⌋.

Bei Palindromen unterscheiden wir zwischen Palindromen gerader Länge und Palindromen ungerader Länge. Bei einem Palindrom gerader Länge befindet sich die Mitte zwischen zwei Zeichen und bei Palindromen ungerader Länge ist ein Zeichen selbst die Mitte. Den Index vor der Mitte bezeichnen wir mit q.

Betrachten wir den String abaacca so ist der Teilstring aba ein maximales Palindrom genauso wie der String aa, beide haben den Radius r = 1. Der Teilstring cc ist ein Palindrom, jedoch ist dieses nicht maximal, da es um die beiden Zeichen rechts und links davon erweitert werden kann. Wir erhalten dann acca als maximales Palindrom mitr = 2.

Beobachtung: Wir unterscheiden zwischen Palindromen und maximalen Palindromen. Ein String T der Länge n und der Form aa…a hat PIC Palindrome ungerader Länge und PIC Palindrome gerader Länge.

Ein String T der Länge n hat genau n maximale Palindrome ungerader Länge und höchstens n-1 maximale Palindrome gerader Länge, da zwei benachbarte Zeichen nicht zwingend die gleichen sein müssen.

Ein String T hat dem nach O(n2) Palindrome, jedoch nur O(n) maximale Palindrome.

Das Palindrom-Problem kann wie folgt definiert werden: Sei T ein String der Länge n, gesucht sind alle maximalen Palindrome in T.

Betrachten wir zunächst den Pseudocode für die naive Bestimmung maximaler Palindrome gerader und ungerader Länge.

In diesem Pseudocode wird jedes maximale Palindrom in T mit der Indexposition q, unmittelbar links von der Palindrommitte bei Palindromen gerader Länge und der Mitte bei Palindromen ungerader Länge, und dem Radius r abgespeichert. Die Lösungen werden jeweils in den zwei Arrays Palodd und Paleven gespeichert. Paleven hat die Größe n - 1 und speichert die Werte für gefundene Palindrome gerader Länge. Gibt es keine Lösung, so wird an dieser Stelle der Wert 0 gespeichert, da es keine Palindrome gerader Länge mit r = 0 gibt. Das Array Palodd hat die Größe n und speichert die Werte für gefundene Palindrome ungerader Länge. Der Radius r = 0 bedeutet hier, dass das Palindrom nur aus einem Zeichen besteht. Die Größen der Arrays ergeben sich jeweils aus der oben bestimmten maximalen Anzahl für maximale Palindrome gerader und ungerader Länge.

Eingabe: T 
 int r 
 Array Pal_even [n-1] 
 Array Pal_odd [n] 
 for q = 0 to n - 1 do 
 
   \\Palindrome gerader Länge 
   r=0 
   while ((q-r)=0) and ((q+r+1)<=n-1) do 
     if not (T[q-r]==T[q+r+1]) then 
	   Pal_even[q]=r 
	   break 
	 \\Zeichenvergleich mit ersten bzw. letzten Zeichen von T 
	 if ((q-r)==0 || (q+r)==n-1) then 
	   Pal_even[q]=r+1 
       break 
    r++ 
 
  \\Palindrome ungerader Länge 
  r=0 
  while ((q-r)= 0) and ((q+r+2)<=n-1) do 
    if not (T[q-r]==T[q+r+2]) then 
      Pal_odd[q+1]=r 
	  break 
    \\Zeichenvergleich mit ersten bzw. letzten Zeichen von T 
	if ((q-r)==0 || (q+r+2)==n-1) then 
	  Pal_odd[q+1]=r+1 
	  break 
	r++ 

Die naive Methode hat O(n) Anfragen und pro Anfrage maximal n 2 Vergleiche, was eine Laufzeit von O(n2) ergibt. Die Gesamtlaufzeit zur naiven Bestimmung maximaler Palindrome gerader und ungerader Länge in T beträgt folglich O(n2).

Wenden wir uns nun der Bestimmung maximaler Palindrome mittels LCA-Anfragen zu. Die Mitte eines Palindroms gerader Länge befindet sich zwischen den Indizes q und q + 1, sodass die r Zeichen nach links, beziehungsweise nach rechts, gelesen die gleichen sind. Betrachtet man nun S und Srev, so beginnen die gesuchten r Zeichen in S an der Position q + 1 und in Srev bei n - q - 2.

In der Vorbereitungsphase des Algorithmus wird in linearer Zeit ein gemeinsamer Suffixbaum für den String S und seine Umkehrung Srev erstellt. In diesem Suffixbaum kann für jedes q von 0 bis n- 2 eine LCE-Anfrage für das Indexpaar (q + 1,n-q - 2) in S, beziehungsweise Srev, in konstanter Zeit beantwortet werden. Die LCE-Anfrage gibt für jedes q den Radius des maximalen Palindroms zurück. Mit Hilfe des Radius und q kann auch das Palindrom ermittelt werden.

Palindrome ungerader Länge haben beim Index q + 1 die Mitte und können durch eine leichte Abänderung des Indexpaares ermittelt werden. Das Indexpaar der LCE-Anfrage ist (q + 2,n - q - 2).

Der folgende Pseudocode zeigt die Berechnung maximaler Palindrome mittels LCE-Anfragen. Zur Erstellung und Vorverarbeitung des Suffixbaumes für S und Srev dient die Methode build_tree(S,Srev).

Max-Palindrom-LCE: 1:

Eingabe: T 
  int r_gerade=0 
  int r_ungerade=0 
  Array Pal_even [n-1] 
  Array Pal_odd [n] 
 
  build_tree(S,Srev) 
 
  for q = 0 to n - 2 do 
    \\Palindrome gerader Länge 
	r_gerade=LCE(q+1,n-q-2) 
	Pal_even [q+1]=r_gerade 
 
	\\Palindrome ungerader Länge 
	r_ungerade=LCE(q+2,n-q-2) 
	Pal_odd [q+1]=r_ungerade 

Die Laufzeit des gesamten Algorithmus setzt sich aus zwei Teilen zusammen. Das Erstellen des Suffixbaumes für LCA-Anfragen nimmt O(n) Zeit in Anspruch. Die Anfragen, welche in konstanter Zeit beantwortet werden können, haben insgesamt eine Laufzeit von O(n). Aus den beiden Laufzeiten ergibt sich eine Gesamtlaufzeit von O(n). Damit ist der folgende Satz bewiesen:

Satz 5.
Alle maximalen Palindrome in einem String T der Länge n können in O(n)ermittelt werden.

paper/least_common_ancestor/section06.txt · Zuletzt geändert: 2012/10/01 18:59 von ben