Suffix Tree & Suffix Array Corso Bioinformatica Francesca Marzi
String matching esatto Data una stringa T (chiamata testo) e una stringa P (chiamata pattern), trovare tutte le occorrenze di P in T. Di solito T > P Un occorrenza di P in T è una sottostringa di T uguale a P. T = ATACATACCCATATACGAGGCATACATGGCGAGTGTGC P = CGAG
String matching esatto T = n, P = m Con preprocessamento Knuth-Morris-Pratt Dato pre-processamento Knuth-Morris-Pratt Con preprocessamento Suffix Tree Dato preprocessamento Suffix Tree Trovare una occorrenza di P O(n+m) O(n) O(n+m) O(m) Se il testo è costante nel tempo (es. genoma), preprocessare (indicizzare) il testo invece del pattern è preferibile (query più efficienti, soprattutto se si deve cercare pattern differenti).
Indicizzazione con Suffissi Approccio: si estraggono suffissi da T. Sviluppate negli anni strutture dati, detti indici, in grado di rappresentare tutti i suffissi di un testo T:
Indicizzazione con Suffissi Suffissi di T = cacao cacao acao cao ao NOTA: ogni sottostringa di T è prefisso di qualche suffisso o ε (suffisso vuoto)
Suffix Trie Albero radicato contenente tutti suffissi del testo Ogni arco è etichettato con un singolo carattere Da uno stesso nodo non esistono due archi uscenti etichettati con lo stesso carattere Ha esattamente n foglie ( T = n) Concatenando i caratteri sul cammino dalla radice ad una foglia, si ottiene un suffisso del testo
Suffix Trie T = abaaba T = 6
Suffix Trie T = abaaba T = 6 Non ha n = 6 foglie I suffissi a, ba, aba sono prefissi di altri suffissi Non corrispondono ad un cammino dalla radice alla foglia
Suffix Trie Si aggiunge un carattere terminale $ alla fine del testo T $ non compare in nessun altra parte in T, e si considera lessicograficamente minore degli altri caratteri Nessun suffisso è prefisso di un altro suffisso Ogni suffisso è rappresentato dal cammino dalla radice ad una foglia T$ = abaaba$
Suffix Trie Si può pensare ai nodi come se avessero una etichetta, ottenuta dalla concatenazione dei caratteri sul cammino dalla radice al nodo stesso
Suffix Trie: ricerca sottostringa Come verifichiamo se una stringa P è una sottostringa di T? Nota: Ogni sottostringa di T è prefisso di qualche suffisso di T Dalla radice si seguono gli archi etichettati con i caratteri in P Se non ci sono archi uscenti per il successivo carattere di P, allora P non è una sottostringa di T Se si consumano tutti i caratteri di P, allora P è una sottostringa di T
Suffix Trie: ricerca sottostringa Come verifichiamo se una stringa P è una sottostringa di T? Nota: Ogni sottostringa di T è prefisso di qualche suffisso di T Dalla radice si seguono gli archi etichettati con i caratteri in P Se non ci sono archi uscenti per il successivo carattere di P, allora P non è una sottostringa di T Se si consumano tutti i caratteri di P, allora P è una sottostringa di T
Suffix Trie: ricerca sottostringa Come verifichiamo se una stringa P è una sottostringa di T? Nota: Ogni sottostringa di T è prefisso di qualche suffisso di T Dalla radice si seguono gli archi etichettati con i caratteri in P Se non ci sono archi uscenti per il successivo carattere di P, allora P non è una sottostringa di T Se si consumano tutti i caratteri di P, allora P è una sottostringa di T
Suffix Trie: ricerca suffisso Come verifichiamo se una stringa P è un suffisso di T? Si usa la procedura precedente, verificando che il nodo finale nel cammino ha un arco uscente etichettato con $
Suffix Trie: ricerca suffisso Come verifichiamo se una stringa P è un suffisso di T? Si usa la procedura precedente, verificando che il nodo finale nel cammino ha un arco uscente etichettato con $
Suffix Trie: numero occorrenze Come contiamo il numero di volte che una stringa P compare come sottostringa in T? Si segue il cammino corrispondente a P: Se questo non esiste la risposta è 0. Se esiste allora si arriva ad un nodo n e la risposta è data dal numero di foglie presenti nel sottoalbero radicato in n
Suffix Trie Come si trova la più lunga sottostringa che si ripete in T? Si cerca il nodo più profondo con più di un figlio
Suffix Trie Numero nodi dell ordine di T 2 nel caso peggiore T = a n b n $
Suffix Tree Suffix Trie compattato Ha esattamente n foglie, etichettate da 1 a n, una per ogni suffisso di T Ogni nodo interno ha almeno due figli Ogni arco è etichettato con una sottostringa non vuota di T Gli archi uscenti da uno stesso nodo hanno etichette che cominciano con caratteri differenti La concatenazione delle etichette degli archi sul cammino dalla radice alla foglia etichettata con i, rappresenta il suffisso T[i n] Può essere costruito in tempo O(n) Occupa spazio O(n) Un pattern di lunghezza m può essere cercato nel Suffix Tree in tempo O(m)
Suffix Trie à Suffix Tree Idea 1: si compattano in un singolo arco tutti i cammini contenenti nodi con un solo arco uscente Riduce il numero di nodi e archi Nodi interni hanno almeno due figli
Suffix Tree La taglia è lineare rispetto ad T = n? Non ancora L = #foglie, I = #nodi interni, E = #archi La taglia del Suffix Tree è definita come L + I + E E = L + I - 1 E 2I (ogni nodo interno ha almeno 2 figli) L + I -1 2I à I L 1 L n (al massimo n suffissi) à I n-1 à E = L + I 1 2n 2 E + L + I 4n 3 = O(n) Ma la lunghezza totale delle etichette degli archi è quadratico in n
Suffix Tree Idea 2: si memorizza il testo T insieme al Suffix Tree e si convertono le etichette degli archi in coppie (offset, length) rispetto a T Adesso lo spazio richiesto è lineare in n O(n) puntatori + testo
Suffix Tree Le foglie sono etichettate dall indice nel testo del corrispondente suffisso
Suffix Tree: ricerca sottostringa Come verifichiamo se una stringa P è una sottostringa di T? Stessa procedura del Suffix Trie
Suffix Tree: ricerca suffisso Come verifichiamo se una stringa P è un suffisso di T? Stessa procedura del Suffix Trie
Suffix Tree: numero occorrenze Come contiamo il numero di volte che una stringa P compare come sottostringa in T? Stessa procedura del Suffix Trie
Costruzione (Naïve Algorithm) Testo T = n, costruisce il Suffix Tree in tempo O(n 2 ) Inserisce in modo incrementale i suffissi T[i n], i = 1,2,,n A i Suffix Tree al passo i, contiene i suffissi che iniziano dalla posizione 1 a i STEP 1: T[1..n] viene inserito in un albero vuoto; A 1 è composto dalla radice r, da un nodo etichettato con 1, e un unico arco, etichettato con T[1..n] tra r ed 1
Costruzione (Naïve Algorithm) STEP i+1: inserimento di T[i+1 n] in A i Dalla radice di A i si cerca il più lungo cammino che fa match con un prefisso di T[i+1...n] NO MATCH nessuno dei precedenti suffissi inizia con il carattere T[i+1] viene creata una nuova foglia numerata con i+1 e viene aggiunto un nuovo arco dalla radice alla foglia i+1, etichettato con T[i+1..n] SI MATCH sia u il nodo in cui termina il match (se il nodo u è implicito lo si rende esplicito splittando l arco su cui si trova) viene creata una nuova foglia etichettata con i+1, e creato un nuovo arco (u,i+1) etichettato con il suffisso di T[i+1,n] che non fa match
Costruzione (Esempio) T = {abcab$} n=6 STEP 1: T[1..6] viene inserito in un albero vuoto, quindi A 1 è composto da un nodo, con etichetta 1, e un unico arco etichettato con T[1..6] dal nodo radice r al nodo 1 T[1..6] = abcab$ A 1 r abcab$ 1
Costruzione (Esempio) T = {abcab$} n=6 STEP 2: T[2..6] : Esiste un cammino in A 1 che è un prefisso di T[2..6]? T[2..6] = bcab$ A 1 r abcab$ 1
Costruzione (Esempio) T$ = {abcab$} n=6 STEP 2: T[2..6] : Esiste un cammino in A 1 che è un prefisso di T[2..6]? NO, viene creata una nuova foglia numerata con 2 e viene aggiunto un nuovo arco dalla radice r alla foglia 2, etichettata con T[2..6] T[2..6] = bcab$ A 1 r A 2 r abcab$ abcab$ bcab$ 1 1 2
Costruzione (Esempio) T = {abcab$} n=6 STEP 3: T[3..6] : Esiste un cammino in A 2 che è un prefisso di T[3..6]? T[3..6] = cab$ A 2 abcab$ r bcab$ 1 2
Costruzione (Esempio) T = {abcab$} n=6 STEP 3: T[3..6] : Esiste un cammino in A 2 che è un prefisso di T[3..6]? NO, viene creata una nuova foglia numerata con 3 e viene aggiunto un nuovo arco dalla radice r alla foglia 3, etichettata con T[3..6] T[3..6] = cab$ A 2 r A 3 r cab$ abcab$ bcab$ abcab$ bcab$ 3 1 2 1 2
Costruzione (Esempio) T = {abcab$} n=6 STEP 4: T[4..6] : Esiste un cammino in A 3 che è un prefisso di T[4..6]? T[4..6] = ab$ A 3 abcab$ r cab$ 3 1 2
Costruzione (Esempio) T = {abcab$} n=6 STEP 4: T[4..6] : Esiste un cammino in A 3 che è un prefisso di T[4..6]? SI, viene creato un nuovo nodo u e una foglia numerata con 4; il nodo u splitta l arco (r,1) nell arco (r,u) e nell arco (u,1); l arco che unisce il nodo u e il nodo 4 viene etichettato con T[6..6] A 3 abcab$ 1 r 2 cab$ 3 A 4 cab$ 1 ab u $ r 4 bcab$ 2 cab$ 3 T[4..6]$ = ab$
Costruzione (Esempio) T = {abcab$} n=6 STEP 5: T[5..6] : Esiste un cammino in A 4 che è un prefisso di T[5..6]? T[5..6] = b$ A 4 ab r cab$ cab$ u $ bcab$ 3 1 4 2
Costruzione (Esempio) T = {abcab$} n=6 STEP 5: T[5..6] : Esiste un cammino in A 4 che è un prefisso di T[5..6]? SI, viene creato un nuovo nodo w e una foglia numerata con 5; il nodo w splitta l arco (r,2) nell arco (r,w) e nell arco (w,2); l arco che unisce il nodo w e il nodo 5 viene etichettato con T[6..6] A 4 cab$ 1 ab u $ r 4 bcab$ 2 cab$ 3 A 5 cab$ 1 ab u $ r 4 b w cab$ $ cab$ 5 2 3 T[5..6] = b$
Costruzione (Esempio) T = {abcab$} n=6 STEP 6: T[6..6] : Esiste un cammino in A 4 che è un prefisso di T[6..6]? T[6..6] = $ A 5 cab$ r ab u $ b w cab$ $ cab$ 5 3 A 6 cab$ ab u $ r b w $ cab$ $ cab$ 5 6 3 1 4 2 1 4 2
Costruzione: Ukkonen Ha la proprietà di essere online Processa il testo simbolo per simbolo da sinistra a destra Ad ogni passo ha il Suffix Tree per la parte di testo già scansionata Osservazione: I suffissi di una stringa T i = t 1 t i possono essere ottenuti dai suffissi della stringa T i-1 = t 1 t i-1 concatenando il t i alla fine di ogni suffisso di T i-1 e aggiungendo il suffisso vuoto Tempo lineare nella lunghezza del testo
Suffix Tree Generalizzato Suffix Tree di un insieme di stringhe Un modo semplice di costruzione consiste nell appendere differenti simboli terminali alle stringhe, concatenare le stringhe e costruire il Suffix Tree della stringa risultante Esempio S 1 = ABAB, S 2 = BABA S 1 = ABAB$ 0 S 2 = BABA$ 1 S = ABAB$ 0 BABA$ 1
Suffix Tree Applicazioni String Matching Esatto Set Matching Esatto Ricerca di una stringa in un database di stringhe (ad esempio, ricerca di una sequenza di DNA in un database di sequenze genomiche) (Suffix Tree generalizzati) Ricerca della sottostringa di lunghezza massima comune a due o più stringhe ) (Suffix Tree generalizzati) Ricerca di DNA contaminato Ricerca di sequenze ripetitive A. Apostolico. The Myriad Virtues of Subword Trees. In Combinatorial Algorithms on Words, 1985 D. Gusfield. Algorithms on Strings, Trees, and Sequences. Cambridge University Press, New York, 1997
Suffix Tree Applicazioni Whole Genome Alignements (Mummer) http://mummer.sourceforge.net Multiple Genome Alignement (MGA) Hohl, Kurtz, Ohlenbusch, Efficient multiple genome alignement, 2002
DAWG & CDAWG Automi a stati finiti che riconoscono i suffissi di una stringa
Suffix Array Array di n interi che specifica l ordine lessicografico dei suffissi di T Introdotti da Manber e Myers nel 1990 Più efficiente in termini di spazio rispetto al Suffix Tree Nella sua forma basica richiede solo 4n bytes (4 bytes per carattere) La costruzione diretta può essere fatta in tempo O(n log n) Puglisi, Smyth, Turpin, A taxonomy of suffix array construction algorithms, ACM Computing Surveys, 39(2):4, 2007
Suffix Array Ordinamento lessicografico di tutti i suffissi Memorizzazione dell indice di partenza del suffisso in un array T$ = cattcat$ T è parte dell indice Suffix Array 0 cattcat$ 7 $ 1 attcat$ 5 at$ 2 ttcat$ 1 attcat$ 3 tcat$ Ordinamento suffissi 4 cat$ 4 cat$ 0 cattcat$ 5 at$ 6 t$ 6 t$ 3 tcat$ 7 $ 2 ttcat$
Suffix Array & Suffix Tree T$=cattcat$ 7 $ 5 at$ 1 attcat$ 4 cat$ 0 cattcat$ 6 t$ 3 tcat$ Posizione nel testo del suffisso Archi uscenti dai nodi ordinati per etichetta Etichette foglie da sinistra a destra 2 ttcat$
Enhanced Suffix Array Ogni algoritmo che usa i Suffix Tree può essere rimpiazzato con un algoritmo che usa i Suffix Array arricchiti con informazioni addizionali (es. LCP array) Si riesce a risolvere gli stessi problemi con la stessa efficienza computazionale Abouelhoda, Mohamed Ibrahim; Kurtz, Stefan; Ohlebusch, Enno (2004). "Replacing suffix trees with enhanced suffix arrays". Journal of Discrete Algorithms 2: 53
Suffix Array: ricerca pattern P per essere sottostringa di T, deve essere prefisso di almeno un suffisso di T Suffissi che condividono un prefisso sono consecutivi nel Suffix Array Ricerca binaria O(m log n) Per trovare tutte le occorrenze si eseguono due ricerche binarie (posizione di start e fine dell intervallo)
Suffix Array: ricerca pattern def search(p): l = 0; r = n while l < r: mid = (l+r) / 2 if P > suffixat(a[mid]): l = mid + 1 else: r = mid s = l; r = n while l < r: mid = (l+r) / 2 if P < suffixat(a[mid]): r = mid else: l = mid + 1 return (s, r) Ricerca Binaria Manber & Myers (1990) Abouelhoda, Kurtz & Ohlebusch (2004) O(m log n) O(m + log n) O(m)
Suffix Array: spazio Spazio lineare in n Tuttavia taglia in bit O(n log n) Testo originale su alfabeto richiede O(n log ) bit Per il genoma umano = 4, n = 3.4 x 10 9 Suffix Array occupa 6 volte più spazio del genoma stesso Compressed Suffix Arrays Compressed Enhanced Suffix Array FM-Index