84
Suffikstræer og deres anvendelser Troels Larsen, Kim Olsen & Morten Poulsen DIKU 19. juni 2006

Su kstrˆer og deres anvendelser - WordPress.com › 2010 › 09 › ...For eksempel, hvis P = bab og T = bababaa, vil vi nde P to steder i T - henholdsvis startende ved position 1

  • Upload
    others

  • View
    2

  • Download
    0

Embed Size (px)

Citation preview

  • Suffikstræer og deres anvendelser

    Troels Larsen, Kim Olsen & Morten Poulsen

    DIKU 19. juni 2006

  • Indhold

    1 Indledning 4

    2 Sammenfatning 5

    3 Introduktion til strengmatchning 6

    4 Notation og terminologi 8

    5 Introduktion til suffikstræer 105.1 Trie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105.2 Patricia-trie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115.3 Suffikstræer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125.4 Eksempel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

    6 Konstruktion af suffikstræer 156.1 En naiv konstruktionsalgoritme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156.2 Lineærtid konstruktionsalgoritmer for suffikstræer . . . . . . . . . . . . . . . . . . 15

    7 Ukkonens algoritme 177.1 Implicitte suffikstræer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177.2 Overblik over Ukkonens algoritme . . . . . . . . . . . . . . . . . . . . . . . . . . . 177.3 Analyse af den naive Ukkonen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187.4 O(n3) → O(n2) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

    7.4.1 Genveje i træet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207.4.2 Fra knude til knude . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227.4.3 Den forbedrede algoritme . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237.4.4 P̊a vej mod en lavere køretid . . . . . . . . . . . . . . . . . . . . . . . . . . 27

    7.5 O(n2) → O(n) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277.5.1 Observation 1: Et blad forbliver et blad . . . . . . . . . . . . . . . . . . . . 277.5.2 Observation 2: Udvidelsesregel 3 terminerer en fase . . . . . . . . . . . . . . 287.5.3 Den enkelte fase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297.5.4 Faserne imellem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307.5.5 Den endelige algoritme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

    7.6 Diskussion af alfabet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

    8 Weiners og McCreights algoritmer 338.1 Weiners algoritme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338.2 McCreights algoritme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338.3 Sammenligning af Ukkonens, McCreights og Weiners algoritmer . . . . . . . . . . . 34

    9 Forskningstendenser siden Ukkonen 359.1 Mindre lagerforbrug: Mere kompakte repræsentationer . . . . . . . . . . . . . . . . 359.2 Mindre lagerforbrug: Alternative strukturer . . . . . . . . . . . . . . . . . . . . . . 359.3 I/O strategier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 369.4 Nye konstruktionsalgoritmer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37

    10 Design af program 3910.1 Repræsentation af et suffikstræ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3910.2 Konstrukion af suffikstræer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4010.3 Implementering af de tre udvidelsesregler . . . . . . . . . . . . . . . . . . . . . . . 4010.4 Online-egenskaben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4110.5 Metoder til afprøvning . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42

    10.5.1 Visualisering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42

    2

  • 10.5.2 Trægennemløb . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4210.6 Programmets struktur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42

    10.6.1 Moduler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4210.6.2 Hierarki . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43

    11 Afprøvning 4411.1 Afprøvning af korrekthed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45

    11.1.1 Design af korrekthedsafprøvning . . . . . . . . . . . . . . . . . . . . . . . . 4511.1.2 Små tekststrenge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4511.1.3 Store tekststrenge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4611.1.4 Samlet konklusion p̊a korrekthedsafprøvning . . . . . . . . . . . . . . . . . 47

    11.2 Afprøvning af effektiviteten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4711.2.1 Resultat af effektivitetsafprøvningen . . . . . . . . . . . . . . . . . . . . . . 48

    A Kildekode 55A.1 node.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55A.2 root.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55A.3 internalnode.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56A.4 leaf.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57A.5 suffixtree.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57A.6 graphvizlayout.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63A.7 main.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66A.8 testprogram.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67

    B GraphViz 71

    C Afprøvning 74C.1 GraphViz korrekthedsafprøvning . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74

    C.1.1 De forventede suffikstræer . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74C.1.2 Suffikstræerne genereret af GraphViz . . . . . . . . . . . . . . . . . . . . . . 76

    C.2 Automatiseret korrekthedsafprøvning . . . . . . . . . . . . . . . . . . . . . . . . . . 78C.3 Grafer for effektivitetsafprøvning . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79

    C.3.1 Grafer for afprøvning af effektiviteten . . . . . . . . . . . . . . . . . . . . . 79

    D Synopsis 81

    3

  • 1 Indledning

    Denne rapport er resultatet af et bachelorprojekt udarbejdet som en del af kurset Datalogi 2B-Bachelorprojekt for̊ar 2006.

    Emnet for projektet er suffikstræer. Et suffikstræ er et træ, der er bygget over en tekststreng,s̊aledes at alle delstrenge af tekststrengen kan findes i træet i tid proportional med længden afdelstrengen. Endvidere er det muligt at finde alle de steder i tekststrengen, hvor delstrengen optræ-der i tid proportional med længden af delstrengen og antallet af steder, hvor delstrengen optræder.

    Rapporten er inddelt i to dele:Første del udvikler et teoretisk fundament til beskrivelse af suffikstræer og beskriver lineær-

    tidsalgoritmer til konstruktion af suffikstræer. I denne del af rapporten vil Gusfield [GUS1999] ogSmyth [SMY2003] blive brugt i et betydeligt omfang til referencer og generel vejledning. Der vilblive lagt vægt p̊a Ukkonens algoritme [UKK1995], da denne kører i lineær tid, er on-line og ansesfor at være den simpleste lineærtidsalgoritme. Første afsnit vil blive afsluttet med et overblik overforskning i suffikstræer, siden Ukkonen fremlagde sin algoritme.

    I anden del designes en implementering af Ukkonens algoritme, og der vil blive udført en analyseog afprøvning af implementeringen. Vi vil koncentrere os, om hvorvidt køretiden og pladsforbrugetaf implementeringen stemmer overens med teorien. Implementeringen vil blive konstrueret til atkunne køre under UNIX p̊a DIKU, vil blive skrevet i C++ og kompileret med gcc version 3.3.4.I denne forbindelse bemærkes det, at ingen af projektdeltagerne har nogen betydelig erfaring i atprogrammere i C++, og det må derfor forventes, at koden ikke er optimal.

    Læseren af rapporten antages at have en datalogisk viden svarende til en 3. års studerende vedDIKU.

    Vi vil gerne takke vores vejledere, lektor Martin Zachariasen og Ph.D-studerende Benny KjærNielsen, for r̊ad og vejleding i udarbejdelsen af denne rapport.

    4

  • 2 Sammenfatning

    Målet med dette projekt var for det første at præsentere suffikstræer og Ukkonens algoritme p̊aen forst̊aelig måde; for det andet at lave en implementering og afprøvning af Ukkonens algoritme;og for det tredje at undersøge anvendelser af suffikstræer (se synopsis i bilag D).

    Det er forsøgt at formulere en forst̊aelig teori til at beskrive suffikstræer (afsnit 4), deresegenskaber (afsnit 5), konstruktionen af disse (afsnit 6, 7 og 8) og at give et overblik over denvidere forskning p̊a omr̊adet (afsnit 9). Der er taget udgangspunkt i [GUS1999] og søgt efteranden litteratur om emnet, hvor specielt [SMY2003] og [GKU1997] kom til at spille en stor rolle.

    Det er søgt at identificere og afklare uklarheder i den brugte litteratur. Fx er det blevet klar-lagt, at en fase i Ukkonens algoritme er skarpt inddelt i tre sekvenser. Vi har ogs̊a søgt at gøre enrække af beviserne mere forst̊aelige og bl.a. givet dem en anden struktur. Generelt i litteraturener der en opfattelse af, at konstruktionen af suffikstræer er svært forst̊aeligt. Det har været voresmål at lave en formidling, der er mere tilgængelig. Vi anser vores mål for at være tilfredsstillendeopn̊aet, men det er naturligvis op til læseren at afgøre.

    Vi har lavet en implementering af Ukkonens algoritme (afsnit 10) og en afprøvning af denne(afsnit 11). I den forbindelse har det været nødvendigt at fokusere enten p̊a pladsforbrug eller p̊akøretid. Vi har valgt at fokusere p̊a køretid.

    Implementeringen er vedlagt i bilag A. Den synes at være korrekt (afsnit 11.1), men er ikkekomplet on-line, da hele inputstrengen indlæses, før der opereres p̊a den. Det viser sig som for-ventet, at køretiden af programmet er lineært, s̊a længe suffikstræet kan ligge i hukommelsen. S̊asnart der ogs̊a skal bruges plads p̊a harddisken, stiger køretiden superlineært (afsnit 11.2). Plads-forbruget synes ogs̊a at være lineært, men er relativt stort, da vi som sagt har prioriteret køretidover lagerplads.

    Der har desværre ikke været tid til at undersøge praktiske anvendelser af suffikstræer i be-tydeligt omfang. Vi har dog implementeret strengmatchning i forbindelse med korrekthedsaf-prøvningen. Strengmatchning er da ogs̊a en vigtig anvendelse af suffikstræet.

    5

  • 3 Introduktion til strengmatchning

    Problemet med at søge efter en eksakt kopi af én streng i en anden streng kaldes for eksaktstrengmatchning. Mere præcist er problemet som følger:

    Givet en streng P kaldet mønstret og en anden streng T kaldet tekststrengen, find ograpportér alle de steder i T hvor P optræder, s̊afremt P optræder i T .

    For eksempel, hvis P = bab og T = bababaa, vil vi finde P to steder i T - henholdsvis startendeved position 1 og 3. Bemærk, som vist i eksemplet, at det er tilladt for to forekomster af P atoverlappe hinanden i T .

    At finde alle forekomster af et mønster i en tekst er et problem, som ofte optræder. Det erfor eksempel et velkendt problem for brugere af tekstredigeringsprogrammer. I dette tilfælde er Ttypisk det dokument, som redigeres, og det P , som søges efter, er et ord indtastet af brugeren. Deter klart, at metoder, som hurtigt rapporterer til brugeren, hvor et givent ord findes i dokumentet,har stor praktisk betydning. Et andet eksempel kunne være informationssøgning p̊a internettetmed brug af søgemaskiner. Ogs̊a inden for andre fagomr̊ader end datalogi optræder strengmatch-ning. For eksempel i biologien, hvor der er behov for at søge efter mønstre i DNA-strenge.

    Eksakt strengmatchning kan gøres naivt ved at gennemløbe tekststrengen og for hver pladsundersøge, om mønstret optræder i tekststrengen med start ved den givne plads. Denne metodekan tolkes grafisk som at føre en kliché af mønstret over tekststrengen. Det har dog vist sig, atdet giver bedre resultater at præprocessere enten mønstret eller tekststrengen, s̊aledes at vi ikkeeksplicit behøver at undersøge hver enkelt plads i tekststrengen. P̊a den måde vil strengmatchning(og den tid, som det tager at lave strengmatchning) best̊a af to dele: en præprocesseringsfase ogen søgningsfase, hvor vi i søgningsfasen drager fordel af observationer fra præprocesseringsfasen,s̊aledes at søgningsfasen foreg̊ar mere effektivt [GUS1999, s.6].

    Klassiske algoritmer til eksakt strengmatchning, fx Knuth-Morris-Pratt (KMP) [KMP1977],Boyer-Moore (BM) [BMO1977] og Rabin-Karp (RK) [KRA1981], præprocesserer mønstret i tidproportional med længden af mønstret for derefter at søge tekststrengen igennem. KMP og BMdrager begge nytte af præprocesseringsfasen i forsøg p̊a at flytte mønstret s̊a hurtigt som muligtlangs tekststrengen fra venstre mod højre; men hvor KMP ved en given plads i tekststrengensammenligner mønstret fra venstre mod højre, sammenligner BM mønstret fra højre mod venstre.RK adskiller sig fra KMP og BM ved ikke at forsøge at flytte mønstret hurtigere langs tekststrengen(springe pladser i tekststrengen over), men i stedet forsøger RK for hver plads i tekststrengenat reducere tiden, der bruges p̊a sammenligning med mønstret. Hvis mønstret har længden m ogtekststrengen længden n, søger KMP i O(n) tid, BM i O(n) tid og RK i O(nm) tid (dog i forventetO(n)). Som sagt bruger alle tre algoritmer O(m) tid til præprocessering.

    Af de klassiske algoritmer til strengmatchning anses BM generelt for at præstere bedst i sæd-vanlige praktiske anvendelser [CLE1997]. B̊ade empiriske og teoretiske undersøgelser bekræfterdette [SMY2003, s.194].

    Med de klassiske algoritmers gode resultater, er det fristende at konkludere at strengmatchninger velløst. Det er umiddelbart korrekt, hvad ang̊ar strengmatchning i relation til tekstredigerings-programmer, da disse programmer sædvanligvis rapporterer resultatet af en søgning i stort setsamme øjeblik, som brugeren sætter søgningen igang. Men n̊ar probleminstanserne vokser, harde klassiske algoritmer svært ved at følge med. Og probleminstanserne bliver stadigt større. Derlagres fx til stadighed mere information p̊a internettet, og diverse databaser (heriblandt DNA-databaser) vokser. Desuden bruges strengmatchning som en delrutine i mange applikationer. S̊adet er rimeligt at antage, at strengmatchning endnu ikke er s̊a effektivt og universielt løst, at detikke kræver yderligere opmærksomhed [GUS1999, s.2-3].

    Brugen af suffikstræer til strengmatchning er et alternativ til de klassiske algoritmer. I den-ne fremgangsmåde præprocesseres tekststrengen i tid proportional med dens længde; i stedet forat præprocessere mønstret som i de klassiske algoritmer. Resultatet af præprocesseringen er et

    6

  • suffikstræ over tekststrengen. Givet et s̊adant suffikstræ kan alle forekomster af et mønster i tekst-strengen findes og rapporteres i tid proportional med mønstrets længde og antallet af forekomster.Hvis mønstret har længden m og tekststrengen længden n, bruger præprocceseringen O(n) tid,mens søgningen bruger O(m+k) tid, hvor k er antallet af gange mønstret optræder i tekststrengen.Da et suffikstræ over en tekststreng kan genbruges til flere søgninger, kommer tidskompleksitetenrigtig til sin ret, n̊ar der søges efter mere end ét mønster i en given tekststreng (da mønstre ofteer væsentlig kortere end tekststrengen).

    Suffikstræer er ogs̊a nyttige til mange flere og mere komplekse problemer end eksakt streng-matchning. Gusfield [GUS1999, kap.7] nævner en lang række, hvor enkelte eksempler er:

    Eksakt sæt-matchning problemet Dette er problemet med at finde alle de steder, hvor møn-strene i et sæt P optræder i en given tekststreng, n̊ar P gives som et samlet input til algo-ritmen (i stedet for at give algoritmen et mønster ad gangen, indtil P er gennemløbet).

    Genkendelse af DNA-defekter Givet en streng S1 (en isoleret sekvens af DNA) og en kendtstreng S2 (mulige kilder af defekt DNA). Find alle delstrenge af S2, der eksisterer i S1, ogsom ikke er længere end en given længde l. Disse delstrenge er kandidater for uønskede deleaf S2.

    Ziv-Lempel datakompression Store tekster eller grafik skal ofte komprimeres for at minime-re brug af lagerplads eller transmissionstid. En populær metode til datakompression afZiv og Lempel [LZI1977, LZI1978] har en effektiv implementering, der bruger suffikstræ-er [EPR1981].

    Approksimativ strengmatchning Dette problem er identisk med eksakt strengmatchning medden forskel, at denne matchning er approksimativ. Approksimativ betyder her, at nogle fejlaf forskellige typer er acceptable i en gyldig forekomst af mønstret.

    Suffikstræer introduceres i afsnit 5. Effektive algoritmer til konstruktionen af suffikstræer ogderes implementering beskrives i de efterfølgende afsnit.

    7

  • 4 Notation og terminologi

    Før vi behandler suffikstræer videre, formaliserer vi i dette afsnit notation og terminologi. Videfinerer her de grundlæggende begreber for at undg̊a enhver tvivl omkring notationens betydning.Vi vil dog kun definere grundlæggende begreber her, og i stedet som hovedregel definere notationog termer, hvor de første gang introduceres i rapporten.

    Definition 4.1. En streng S er et array best̊aende af tegn taget fra et alfabet Σ og noteres entenS[1..|S|] eller blot S, hvor |S| angiver længden af S. Bemærk at den første plads i S starter ved 1.

    Definition 4.2. Givet en streng S defineres følgende for alle i, j ∈ {1,2,...,|S|}, i ≤ j:

    a) S[i..j] er den delstreng af S, som starter ved position i og slutter ved position j, beggeinklusive.

    b) S[1..i] er det præfiks af S, som slutter ved position i, og S[i..|S|] er det suffiks af S, somstarter ved position i.

    c) S[i] er det tegn, som er ved position i i S. Det er ensbetydende med S[i..i].

    Definition 4.3. Vi noterer den tomme streng med � og definerer S[i..j] til at være den tommestreng hvis i > j. Den tomme streng er et præfiks og suffiks af enhver streng og tilhører ethvertalfabet Σ.

    Strenge og tegn skrives i kursiv. Vi noterer tegn med små typer (a, b, c etc.) og variable strengemed små græske typer (α, β, γ etc.). Sammenkædning af to strenge α og β skrives αβ, har længden|α| + |β| og indeholder tegnene fra α efterfulgt af tegnene fra β.

    Med brug af variable strenge og sammenkædning kan vi udtrykke delstrenge, præfiks og suffiksp̊a en anderledes måde:

    ◦ β er en delstreng af S, hvis S = αβγ hvor α, γ måske er tomme.

    ◦ β er et præfiks af S, hvis S = βγ hvor γ måske er tom.

    ◦ β er et suffiks af S, hvis S = αβ hvor α måske er tom.

    Som nævnt i definition 4.3 kan β ogs̊a være tom. Et ægte præfiks, suffiks eller delstreng eret præfiks, suffiks eller delstreng, henholdsvis, som hverken er hele strengen eller tomt. Lad oskonkretisere definitionerne gennem et eksempel. For strengen S = axby gælder (blandt andet)

    ◦ |S| = 4 s̊a S kan ogs̊a skrives som S[1..4], og vi har ogs̊a S[2..3] = xb, S[4] = y, xb er endelstreng af S, ax er et præfiks af S, xby er et suffiks af S, og S kan skrives som axγ hvorγ = by.

    Før vi kan definere strengmatchning-problemet, mangler vi at definere, hvorn̊ar tegn og strengematcher, og hvorn̊ar et mønster optræder i en tekststreng.

    Definition 4.4. Ved sammenligninger af tegn og strenge gælder følgende.

    a) Ved en sammenligning af to tegn siger vi, at tegnene matcher, hvis de er ens; og at de ikkematcher ellers.

    b) Ved en sammenligning af to strenge, S1 og S2 af længde n, siger vi, at strengene matcher,hvis deres tegn matcher indbyrdes; alts̊a hvis der gælder S1[i] = S2[i] for 1≤ i ≤ n; og at deikke matcher ellers. N̊ar to strenge matcher, skriver vi S1 = S2.

    Definition 4.5. Lad et mønster P [1..m] og tekststreng T [1..n] være givet, hvor 1≤ m ≤ n. Visiger, at P optræder i T ved position i, hvis T [i + k − 1] = P [k] for 1 ≤ k ≤ m.

    Hvis P optræder i T ved position i, siger vi ogs̊a, at der er en forekomst af P ved position i iT .

    8

  • Bemærk at P og T er ikke-tomme strenge. Og yderligere at m ≤ n, for hvis m > n optræderP trivielt ikke i T . Vi definerer nu formelt eksakt strengmatchning.

    Definition 4.6. Givet et mønster P og en tekststreng T , er eksakt strengmatchning problemetmed at finde og rapportere alle de positioner i i tekststrengen T , hvor mønstret P optræder.

    Vi bruger symbolet S for den generelle streng med længde n. N̊ar S er et mønster, skriver viP , som har længden m. N̊ar S er en tekststreng, skriver vi T , som har længden n.

    9

  • 5 Introduktion til suffikstræer

    For at give den mest intuitive introduktion til suffikstræer gives der i dette afsnit en kort intro-duktion til de to datastrukturer, der er forløbere for suffikstræet - trie strukturen fra 1960 ogPatricia-trie fra 1968. Dette gøres i kronologisk rækkefølge med fokus p̊a de basale koncepteri datastrukturerne og det brede overblik. Denne tilgang og en række af figurerne er inspireretaf [SMY2003, kap.2].

    5.1 Trie

    Datastrukturen trie blev introduceret i 1960 af E. Fredkin [FRE1960] og er et træ baseret p̊a etsæt af strenge, hvor træets kanter repræsenterer strengenes tegn.

    Givet et sæt af strenge X = {x1, x2, ..., xm} opbygges et trie ved, at hver streng indsættesmed udgangspunkt i roden af træet, hvor hver kant repræsenterer ét tegn i strengen. S̊afremt derallerede eksisterer en udg̊aende kant fra roden med det p̊agældende tegn, følges kanten til næsteknude, hvorefter indsættelsesprocessen fortsætter fra næste tegn i strengen. N̊ar hele strengen ergennemløbet, markeres den knude, hvor indsættelsen sluttede. Figur 1 viser et trie over sættet X= {c, cpp}. Knuder, hvor en indsættelse sluttede, markeres med ikke-fyldte prikker.

    Det er herefter muligt i lineær tid i længden af et givent mønster at bestemme, om mønstretfindes i sættet X . Det gøres ved at tage udgangspunkt i roden af træet og følge mønstret, tegnfor tegn, ned gennem træets knuder. Hvis der i en knude ikke er en udg̊aende kant med detønskede tegn, findes mønstret ikke i træet. Omvendt hvis alle mønstrets tegn kan matches op modtilhørende kanter i træet, og matchningen slutter i en markeret knude, findes mønstret i X .

    Figur 1: Trie over sættet X = {c, cpp}.

    Før en dybere gennemgang af denne struktur finder sted defineres her nogle praktiske termer,som vil være til nytte senere.

    Definition 5.1. En kantmærkat best̊ar af de tegn, der er skrevet p̊a den p̊agældende kant. Enknudes stimærkat er en sammenkædning af kantmærkaterne p̊a kanterne i en traversering fra rodentil knuden. En knude betegnes som en terminalknude, s̊afremt dens stimærkat repræsenterer enstreng i X , hvor X er det sæt af strenge, som danner basis for træet.

    For at sikre at alle strengene altid er eksplicit repræsenterede som blade, kan vi modificerestrengene ved at tilføje et unikt tegn $ i slutningen af hver streng. Et unikt tegn er i dennesammenhæng et tegn, der ikke eksisterer i alfabetet, som strengene er genereret over. Hermedvil en terminalknude ikke samtidigt være en intern knude. Den uheldige situation opst̊ar, hvisen streng i træet er et præfiks for en anden streng i træet, hvorved præfiksstrengen vil have enterminalknude inden i den anden streng. Det er tilfældet for figur 1, da c er et præfiks af cpp. Ifigur 2(a) ses triet over det modificerede sæt X = {c$, cpp$}.

    Ved at tilføje et unikt tegn $ for enden af hver streng sikres, at dette problem ikke opst̊ar, daingen streng nu kan være præfiks af en anden streng. I den forbindelse defineres desuden, at $ ikkeindg̊ar implicit i en streng S og i de tilfælde, hvor vi afslutter S med det unikke tegn, vil det blivenoteret eksplicit som S$.

    10

  • (a) Trie over X. (b) Patricia-trie over X.

    Figur 2: Trie og Patricia-trie over sættet X = {c$, cpp$}.

    Det eneste tidspunkt, hvor en søgning nu ikke stopper i et blad, er hvis det mønster derledes efter ikke eksisterer i X eller hvis der specifikt ledes efter præfikser af en streng i X . Hvissidstnævnte er tilfældet, skal det blot huskes, at mønstret godt kan eksistere i træet, selvomsøgningen ikke slutter i en terminalknude.

    Generelt er der m + 1 terminalknuder i et trie, da antallet af terminalknuder er lig antallet afstrenge i sættet X , plus én for den tomme streng. Givet tilføjelsen af det unikke tegn til strengenekan det sikres, at de m + 1 terminalknuder ogs̊a er blade.

    P̊a vores vej mod suffikstræet introduceres i næste afsnit en efterfølger til triet, som har desamme egenskaber som triet, men som kan repræsenteres mere kompakt. Det resulterer bl.a. i enlidt hurtigere traversering igennem træet vha. en teknik kaldet knudehop, som omtales i afsnit7.4.2.

    5.2 Patricia-trie

    Datastrukturen Patricia-trie blev præsenteret i 1968 af D.R. Morrison [MOR1968] som et kompakttrie. Et Patricia-trie er et trie, der er gjort mere kompakt ved at udelade den eksplicitte kantre-præsentation af hvert tegn. I stedet indeholder hver kant potentielt k tegn, hvor k er antallet aftegn i den længste streng i X . Det konstrueres med udgangspunkt i et trie ved at eliminere alleinterne knuder med kun én udg̊aende kant, se figur 2(b).

    Et Patricia-trie har i øvrigt identiske egenskaber med et klassisk trie med undtagelse af præ-fikssøgning, der ikke længere kan garantere, at et præfiksmønster stopper i en knude. Det skyldes,at kanterne kan indeholde flere tegn, hvilket medfører, at en søgning kan stoppe midt p̊a en kant.Det er fx tilfældet for søgningen efter præfikset cp til strengen cpp i X (se figur 3). I denne figurangiver den fede markering søgningen. Bemærk at søgningen (den fede markering) stopper midt ien kant.

    Figur 3: Søgning efter cp i Patricia-triet over sættet X = {c$, cpp$}.

    Mulighederne med Patricia-triet er mange, men vi vil koncentrere os om suffiksvariationen. Herindskrænkes inddataen til én enkelt streng og dens suffikser. Til gengæld opn̊as en mere effektiv

    11

  • implementering af konstruktionsalgoritmen som følge af suffiksernes indbyrdes relation. Dennespecialisering af Patricia-triet kaldes for et suffikstræ.

    5.3 Suffikstræer

    Suffikstræet, som blev introduceret af Weiner [WEI1973], er som sagt et Patricia-trie baseret p̊aén enkelt streng og dens respektive suffikser. Alts̊a hvor sættet X = {x1, x2, ..., xm} tager formenX = {S[1..n], S[2..n], ..., S[n]} for én streng S.

    Som nævnt har et suffikstræ derfor identiske egenskaber med et Patricia-trie samt nogle yder-ligere fordele, som skyldes at strengene i X (suffikserne til en given streng) har nogle indbyrdesrelationer, som ikke er til stede i et generelt sæt X for et klassisk Patricia-trie. Et suffikstræ kanogs̊a defineres uafhængigt af Patricia-triet p̊a følgende vis [GUS1999, s.90]:

    Definition 5.2. Et suffikstræ T for en n-tegn lang streng S er et træ med n blade nummereret fra1 til n. Hver intern knude har mindst to børn, og hver kant er markeret med en ikke-tom delstrengaf S. Hertil kommer, at to udg̊aende kanter ikke kan have identiske førstetegn. Nøgleegenskaben iet suffiks træ er, at sammenkædningen af tegnene p̊a kanterne fra roden til bladet med nummereti, resulterer i det suffiks af S, der begynder ved position i.

    Til denne definition bemærkes, at indsættelsen af suffikstræet kun har n blade i det tilfælde,hvor S har et unikt sluttegn. Da det unikke tegn forhindrer, at et suffiks er et præfiks af et andetsuffiks. For at sikre at dette altid er tilfældet, afsluttes alle strenge med et unikt tegn som benævnes$. Desuden medfører tilføjelsen, at suffikstræet i stedet for n blade, nu har n + 1 blade.

    Foruden egenskaberne i ovennævnte definition, følger der endnu en heldig egenskab til suffiks-træet, der omhandler identifikation af delstrenge. P̊a grund af de interne suffiksrelationer mellemstrengene i suffikstræet gælder der, at enhver given delstreng af S vil være et præfiks af et suffikstil S. Og da præfikssøgning allerede er en etableret egenskab for træstrukturen, medfører dette, atalle delstrenge af S kan identificeres p̊a lige fod med præfikser. Desuden medfører positionsnume-reringen i bladene, at det er muligt at identificere alle forekomster af delstrengene i S, samt hvorde optræder i den oprindelige streng S.

    Se figur 4(a) for et eksempel p̊a et suffikstræ. I næste afsnit gives et eksempel p̊a søgning idette træ.

    (a) Suffikstræet med eksplicit repræsentation aftegn p̊a kanter.

    (b) Suffikstræet med heltels-repræsentation aftegn p̊a kanter.

    Figur 4: Suffikstræer for S = kakao.

    Et suffikstræ indeholder maksimalt 2n+1 knuder og 2n kanter, da der er n+1 blade i træet (etfor hvert suffiks samt termineringstegnet $) og maksimalt n interne knuder (da hver intern knudeminimum har to udg̊aende kanter). Det resulterer i et lineært antal elementer i træet, hvilketmuliggør et lineært pladsforbrug for træet. Dette er en vigtig egenskab, da et lineært pladsforbrugmuliggør en lineær konstruktionstid. Det lineære antal elementer resulterer dog kun i et lineærtpladsforbrug, s̊afremt knuderne og kanterne ligeledes har et lineært pladsforbrug. Dette er dog ikketilfældet, hvis tegnene eksplicit repræsenteres p̊a kanterne, da kanterne dermed potentielt optagerkvadratisk lagerplads. Fx for en streng S[1..n] hvor hvert suffiks starter med et distinkt tegn. Her

    12

  • vil træet best̊a af roden og n forskellige kanter ud fra roden med længderne n, n − 1,...,1, hvilketresulterer i en kvadratisk størrelse (n + n − 1 + ... + 1 = n(n+1)2 ). Denne argumentation gælderkun, hvis |S| ≤ |Σ|, men resultatet holder stadig hvis |S| > |Σ| [GUS1999, s.104].

    Problemet kan løses ved at tage udgangspunkt i, at kanterne i suffikstræet potentielt best̊ar afn tegn. Derfor er det ikke praktisk at repræsentere hvert enkelt tegn p̊a kanterne som for triet. Istedet anvendes par af heltal, der specificerer den respektive position af kantmærkaten i forholdtil tegnenes position i S. Se figur 4(b) for et eksempel p̊a et suffikstræ med heltalsrepræsentation.

    Denne datarepræsentation har et lineært pladsforbrug (da hver kant optager konstant plads),hvorved det ønskede mål er n̊aet. Ulempen er, at det kræver, at S lagres eksplicit.

    5.4 Eksempel

    For at give en intuitiv forst̊aelse af suffikstræet og dets muligheder gennemg̊as her et eksempel,som løser det klassiske strengmatchning-problem for et mønster P og en tekststreng T .

    Fremgangsmåden for at løse dette problem er opdelt efter den klassiske todeling inden forstrengmatchning: præprocessering og søgning. Præprocesseringen best̊ar af konstruktionen af suf-fikstræet. Dette kan gøres i O(n) tid, og algoritmer til dette bliver gennemg̊aet i afsnit 6 til 8.Søgningen er baseret p̊a definition 5.2 og tager udgangspunkt i det faktum, at en søgning nedigennem et suffikstræ resulterer i en unik streng.

    Søgeprocessen starter i roden af træet og matcher tegnene i P med kanterne i suffikstræet,indtil hele P er gennemløbet eller det ikke er muligt at matche flere af tegnene i P. Denne strenger et suffiks af T, s̊afremt et blad n̊as og en delstreng af T , hvis traverseringen ender i en kant elleren intern knude. Hvis hele P bliver gennemløbet er resultatet et af de ovenst̊aende to, dvs. P er etsuffiks af T eller en delstreng af T. Omvendt hvis P ikke er fuldkomment gennemløbet, og ingenvidere matchning er mulig, er P ikke repræsenteret i teksten T.

    Som eksempel er anvendt teksten T = kakao$ og en række forskellige mønstre P = kao$,P = aa, P = ka og P = aka.

    Eksempel 1 P = kao$. Fra roden identificeres kanten med mærkat ka. Dernæst identificereskanten med mærkat o$, hvorved vi ender i blad 3. Det fortæller os, at P eksisterer i T samtat P er et suffiks til T, der starter ved position 3 (se fig. 5(a)).

    Eksempel 2 Havde vi valgt P = aa, var vi endt i en intern knude uden et fuldt gennemløb af P,hvorfor det kan konkluderes, at P = aa ikke eksisterer i T (se fig. 5(b)).

    Eksempel 3 Havde vi valgt P = ka, var vi endt i en intern knude med et fuldt gennemløb af Pog kunne derfor konkludere, at mønstret eksisterer i T. Positionerne p̊a alle forekomsterne afmønstret kan identificeres ved at lave en traversering af undertræet ved den interne knudeog rapportere alle blade p̊a vejen. Vi finder P ved position 1 og 3. S̊afremt kun den førsteposition af mønstret ønskes, kan denne information (bladets nummer) gemmes i den interneknude (dermed vil en traversering kunne undg̊as) (se fig. 5(c)).

    Eksempel 4 Havde vi valgt P = aka, var vi endt i en kant med fuldt gennemløbet P , hvilketindikerer, at P = aka eksisterer i T som en delstreng. Positionen kunne findes p̊a sammemåde som i ovenst̊aende tilfælde (se fig. 5(d)). Vi finder P ved position 2.

    13

  • (a) Søgning efter kao$. (b) Søgning efter aa.

    (c) Søgning efter ka. (d) Søgning efter aka.

    Figur 5: Søgninger.

    14

  • 6 Konstruktion af suffikstræer

    Suffikstræet er, som det blev vist i forrige afsnit, i teorien en nyttig datastruktur. Spørgsmålet er,om det i praksis er en brugbar datastruktur. Svaret hænger bl.a. sammen med, om et suffikstrækan konstrueres med en lav tidskompleksitet.

    I afsnit 6.1 vil en naiv O(n2)-algoritme til konstruktion af suffikstræer blive præsenteret, og iafsnit 6.2 vil tre lineærtids konstruktionsalgoritmer blive beskrevet overfladisk. I afsnit 7, 8.1 og8.2 vil der blive g̊aet mere i dybden med hver af disse tre algoritmer.

    Vi vil i det følgende antage, at størrelsen af alfabetet er konstant. I afsnit 7.6 vil vi overvejedenne antagelse nærmere.

    6.1 En naiv konstruktionsalgoritme

    En umiddelbar metode til konstruktion af et suffikstræ over en tekststreng S beskrives i [GUS1999,s.93]. Algoritmen tager udgangspunkt i, at man indsætter suffikserne et ad gangen. Først indsættesdet længste suffiks S[1..n]$. Derefter indsættes det næstlængste suffiks, og s̊a fremdeles.

    Indsættelsen af suffikset S[i..n]$, hvor 2 ≤ i ≤ n, gøres ved at starte i roden i det ufærdigesuffikstræ. Herfra findes den længste sti, der er et præfiks af suffikset S[i..n]$. Stien findes vedsuccessivt at sammenligne tegn i suffikset med tegn i træet, indtil to tegn er forskellige. Hvis detteikke sker i en knude, skabes en ny knude, og kanten deles i to. Uanset om der er blevet skabt enny knude eller ej, skal der laves en ny kant fra den knude, hvor der skete en mismatch. Denne kantslutter i et blad, der f̊ar tildelt værdien i. Algoritmens kørsel er illustreret i fig. 6.

    Indsættelse af et suffiks i et ufærdigt suffikstræ er entydigt, idet der i hver knude højest er enkant videre for hvert tegn i alfabetet, jf. definition 5.2.

    Eftersom der maksimalt skal laves n − i sammenligninger pr. indsættelse, og der er n − 1indsættelser, må antallet af operationer være proportionalt med n2, og dermed er køretiden forden naive algoritme O(n2).

    (a) Oprettelse af kakao$. (b) Indsættelse af akao$. (c) Indsættelse af kao$.

    (d) Indsættelse af ao$. (e) Indsættelse af o$. (f) Indsættelse af $.

    Figur 6: Naiv konstruktion af kakao$.

    6.2 Lineærtid konstruktionsalgoritmer for suffikstræer

    Den første lineærtid konstruktionsalgoritme for suffikstræer blev opfundet af Weiner [WEI1973] i1973. I 1976 konstruerede McCreight [MCC1976] en anden lineærtid algoritme, der brugte mindreplads. I 1995 foreslog Ukkonen [UKK1995] en ny lineærtid algoritme, der siden har vist sig at væretæt beslægtet med de to andre algoritmer [GKU1997].

    15

  • En algoritme er on-line, hvis den i hver iteration giver en korrekt løsning for den del af inputtet,der er indlæst. En væsentlig fordel ved denne egenskab er, at man ikke behøver at inddrage tidligereeller senere dele af inputtet, n̊ar man regner p̊a position i.

    For konstruktion af suffikstræer betyder dette, at man i hver iteration af algoritmen konstrue-rer et korrekt suffikstræ for de i første tegn i strengen S. Man kan alts̊a afslutte algoritmens kørselp̊a et vilk̊arligt tidspunkt og f̊a et korrekt suffikstræ for den del af inputstrengen, der er behandlet.Dette er en ganske væsentlig egenskab. Ukkonens algoritme er den eneste af de tre algoritmer, derer on-line.

    Weiners algoritme [WEI1973] starter med at indsætte det korteste suffiks, S[n]$, i træet. Der-efter indsættes suffikserne S[n − i..n]$, for i stigende fra 1 til n − 1, indtil alle suffikser er indsati træet. Ved hjælp af en række tricks kan man f̊a Weiners algoritme til at køre i lineær tid, mendet har en pris: Hver knude i træet indeholder to store tabeller, hvorved algoritmen bliver megetpladskrævende. Efter indsættelse i haves et korrekt suffikstræ for de sidste i tegn i tekststrengen.Dette kaldes ogs̊a en anti-on-line egenskab [GKU1997].

    McCreights algoritme [MCC1976] starter som den naive algoritme med at indsætte det længstesuffiks i suffikstræet, hvorefter suffikserne S[i..n]$ successivt indsættes for i voksende fra 2 tiln. Ganske som i Weiners algoritme kan man bruge en række tricks til at gøre køretiden lineær,og pladsforbruget er væsentligt bedre end med Weiners algoritme. Prisen er, at man først f̊ar etkorrekt suffikstræ, n̊ar algoritmen er løbet til ende.

    Ukkonens algoritme [UKK1995] indsætter, til forskel fra de to andre algoritmer, tekststrengenspræfikser i suffikstræet. Først indsættes det korteste præfiks, S[1], og derefter indsættes voksendepræfikser, dvs. S[1..i] for i voksende fra 2 til n. Til sidst indsættes $, og herved f̊aes det færdigesuffikstræ. Man f̊ar først et korrekt suffikstræ, n̊ar $ er indsat. Efter hver iteration kan konstruk-tionen af suffikstræet afsluttes, og hvis man indsætter $, kan man f̊a et korrekt suffikstræ for dentekststreng, der best̊ar af de allerede indsatte tegn. Ukkonens algoritme er derfor on-line. Gennemen række tricks kan køretiden gøres lineær. Pladsforbruget ligger p̊a linie med McCreights algorit-me.

    Ukkonens algoritme har alts̊a lineær køretid, lineært pladsforbrug og er on-line. Disse treegenskaber gør Ukkonens algoritme meget interessant, og derfor vil vi g̊a i dybden med dennealgoritme i næste afsnit.

    Det viser sig, jf. [GKU1997], at de tre algoritmer grundlæggende bruger de samme operationerog tricks for at opn̊a lineær køretid. Det bemærkes, at McCreights algoritme kører en smulehurtigere (b̊ade i teori og praksis) end Ukkonens algoritme, hvilket hænger sammen med Ukkonenson-line egenskab. Se afsnit 8.3 for en uddybning af dette.

    16

  • 7 Ukkonens algoritme

    Da Ukkonen introducerede sin algoritme i [UKK1995], tog han udgangspunkt i endelige automater.Denne tilgang anser vi for at være svært tilgængelig. Jf. [SMY2003, s.296] kan et træ og specieltet suffikstræ anses for at være en endelig automat. Derfor kan vi tillade os at bruge en andentilgangsvinkel, som følger Gusfields [GUS1999, afs.6.1] - nemlig træ-tilgangen.

    Først gennemg̊aes en naiv formulering af Ukkonens algoritme, der kører i O(n3) tid. Derefteranalyseres en række flaskehalse, som senere afsnit vil rette op p̊a: I afsnit 7.4 bruges suffiksgenvejeog knudehop-observationen til at forbedre køretiden til O(n2), og i afsnit 7.5 bruges en rækkeobservationer til at optimere algoritmen, s̊a den kan køre i lineær tid.

    Den færdige algoritme vil blive præsenteret i afsnit 7.5.5.

    7.1 Implicitte suffikstræer

    Ukkonens algoritme konstruerer en følge af implicitte suffikstræer, hvor det sidste bliver konverterettil det færdige suffikstræ for tekststrengen S$. Derfor vil vi starte med at definere et implicitsuffikstræ [GUS1999, s.94]:

    Definition 7.1. Et implicit suffikstræ I for en streng S konstrueres ved at lave følgende ændringeri suffikstræet for S:

    a) fjerne alle forekomster af $ fra kantmærkater,b) fjerne alle kanter, der ikke har nogen mærkat, ogc) fjerne alle knuder, der kun har ét barn.

    Man kan ogs̊a anskue det implicitte suffikstræ for S som det træ, der opst̊ar, n̊ar man opbyggeret suffikstræ for strengen S uden at indsætte sluttegnet $. Et eksempel kan ses i figur 7. Bemærkat fx suffikset te i det implicitte suffikstræ ikke ender i et blad, men i stedet midt p̊a en kant.

    (a) Suffikstræet for S$. (b) Implicit suffikstræ for S.

    Figur 7: Suffikstræ og implicit suffikstræ for S = teste.

    7.2 Overblik over Ukkonens algoritme

    Ukkonens algoritme er inddelt i n faser, hvor n er længden af den tekststreng S, som suffikstræetopbygges over. I fase i opbygges et implicit suffikstræ over strengen S[1..i] fra det implicittesuffikstræ, der blev konstrueret i forrige fase i − 1. I hver fase tilføjes alts̊a et nyt præfiks tilsuffikstræet. Det første implicitte suffikstræ, I1, konstrueres ved at bygge et træ best̊aende af enrod, en kant og et blad. Kanten har kantmærkat S[1] og bladet har værdien 1.

    Hver fase inddeles i i udvidelser, en for hver af de i suffikser af præfikset S[1..i]. I udvidelse jaf fase i finder algoritmen først enden af den sti, der har stimærkat S[j..i − 1] og sikrer, at S[i]optræder for enden af denne sti. I denne situation er der tre forskellige scenarier. Algoritmen kanhave n̊aet stiens slutning i et blad, der derfor skal udvides. Algoritmen kan ogs̊a være n̊aet indi en kant eller en knude, og der skal derfor laves en ny kant til et nyt blad, og måske en knude.

    17

  • Det kan ogs̊a ske, at suffikset S[j..i] allerede findes i suffikstræet. Da Ukkonens algoritme brugerimplicitte suffikstræer, skal algoritmen i dette tilfælde blot fortsætte til næste udvidelse uden atændre træet.

    For at kvalificere disse betragtninger definerer [GUS1999, s.96] et sæt af udvidelsesregler. I detfølgende er S[j..i − 1] et suffiks af S[1..i − 1], og det antages, at vi er i fase i i udvidelse j.

    Udvidelsesregel 1 I det nuværende træ ender stien S[j..i − 1] i et blad. Her skal S[i] indsættesi slutningen af kantens stimærkat for at konstruere det nye træ.

    Udvidelsesregel 2 Der er ingen sti fra enden af S[j..i − 1] , der starter med S[i].a) Slutter S[j..i − 1] i en kant skal kanten splittes op i to, og der skal oprettes en knude p̊adette sted. Derefter gøres som i b).b) Slutter S[j..i − 1] i en knude skal der blot oprettes en kant fra knuden med S[i] somstimærkat. Kanten skal slutte i et blad, der har nummer j.

    Udvidelsesregel 3 Der eksisterer allerede en sti fra enden af S[j..i− 1], der starter med S[i], ogder skal derfor ikke gøres noget.

    Ud fra disse overvejelser kan pseudokoden for den naive version af Ukkonens algoritme præsen-teres, se Algoritme 1. I figur 8 ses det overordnede forløb for Algoritme 1 p̊a strengen S = kakao$.I figur 9 ses en mere detaljeret gennemgang af fase i = 5, hvor S[5] = o indsættes.

    Algoritme 1 Naiv Ukkonen

    1: Konstruér træ I1.2: for i from 2 to n do {start fase i}3: for j from 1 to i do {start udvidelse j}4: Find enden af stien fra roden med stimærkat S[j..i − 1].5: Med brug af en passende udvidelsesregel, opdatér træet for at sikre at S[i] optræder ved

    enden af S[j..i − 1]:6: if enden er i et blad then7: udvid bladkantens mærkat med S[i] (UR1).8: else if enden ikke efterfølges af S[i] og enden er i en kant then9: opdel kanten i to, indsæt en ny knude v og en ny kant med mærkat S[i] fra v (UR2a).

    10: else if enden ikke efterfølges af S[i] og enden er i en knude then11: indsæt en ny kant med mærkat S[i] fra knuden (UR2b).12: else13: enden efterfølges af S[i], gør intet (UR3).14: end if15: end for16: end for17: Tilføj $ til S og fortsæt algoritmen med dette tegn.

    7.3 Analyse af den naive Ukkonen

    Af udvidelsesreglerne ses, at n̊ar først enden af stien S[j..i − 1] er fundet, kan en given udvidelseudføres i konstant tid (under forudsætning af konstant alfabetstørrelse).

    Køretiden hænger alts̊a p̊a, hvor mange gange udvidelsesreglerne bliver kaldt, og hvor længedet tager at finde frem til de steder, hvor der skal udvides. Vi ser, at der er n faser, og hver fasehar i udvidelser, hvor i vokser fra 1 til n. For hver udvidelse skal enden af stien S[j..i − 1] findes.Dette kan (naivt) gøres ved at søge ned gennem suffikstræet fra roden, hvilket vil tage O(i− j +1)tid. Fase i tager s̊aledes O(i2) tid, og køretiden er s̊a O(n3).1

    1Dette kan ogs̊a indses ved beregningerne:Pn

    i=1

    Pij=1(i−j+1) =

    Pni=1(i

    2+i− i2+i2

    ) = n(n+1)(2n+1)12

    + n(n+1)4

    18

  • (a) Fase 1. (b) Fase 2. (c) Fase 3.

    (d) Fase 4. (e) Fase 5. (f) Fase 6.

    Figur 8: Overordnet forløb for Algoritme 1 p̊a strengen S = kakao$.

    (a) Udv. 1: søg efter S[1..4]. (b) Udv. 2: søg efter S[2..4]. (c) Udv. 3: søg efter S[3..4].

    (d) Udv. 4: søg efter S[4..4]. (e) Udv. 5: søg efter S[5..4] = �.

    Figur 9: Fase i = 5 for Algoritme 1 p̊a strengen S = kakao$. Træet I4 skal udvides med tegn S[5].

    19

  • Denne analyse giver en række væsentlige indsigter i, hvordan den naive form af Ukkonensalgoritme kan forbedres.

    For det første skal det implicitte suffikstræ søges igennem ved hver indsættelse. Kan man findeen smartere måde at søge træet igennem p̊a, kan køretiden forbedres væsentligt.

    For det andet tager hver udvidelse konstant tid. Algoritmens køretid har alts̊a en nedre grænse,der er proportional med antallet af udvidelser. Dermed kan en lineærtid konstruktionsalgoritmekun konstrueres, hvis antallet af udvidelser er lineært proportionalt med antallet af tegn i tekst-strengen.

    I den følgende gennemgang vil der blive beskrevet løsninger p̊a hver af disse flaskehalse, s̊aledesat vi i slutningen af afsnittet kan præsentere Ukkonens algoritme i sin endelige form.

    7.4 O(n3) → O(n2)Som nævnt har det stor indflydelse p̊a køretiden af Ukkonens algoritme, hvor effektivt enden afalle stierne S[j..i− 1] kan findes i træet. I dette afsnit introduceres to metoder, som gør søgningenefter en sti S[j..i− 1] i det implicitte suffikstræ hurtigere. Den første beskrives i afsnit 7.4.1 og vilgøre, at vi ikke behøver at starte alle søgninger fra roden. Den anden beskrives i afsnit 7.4.2 ogvil gøre, at vi ikke behøver at undersøge en kant tegn for tegn, men i stedet kan springe fra knudetil knude.

    7.4.1 Genveje i træet

    Indtil nu har vi startet en søgning efter enden af en sti S[j..i − 1] fra roden. Som vist i afsnit 7.3resulterer det i en køretid p̊a O(i2) for en fase i. Metoden beskrevet i dette afsnit kan (i samarbejdemed metoden fra afsnit 7.4.2) reducere denne tid. Metoden baserer sig p̊a begrebet suffiksgenveje,s̊a lad os først introducere dette begreb og dets relation til det implicitte suffikstræ.

    SuffiksgenvejeEn suffiksgenvej defineres som følger [GUS1999, s.98]:

    Definition 7.2. Lad v være en intern knude med stimærkat xα. Hvis der eksisterer en andenintern knude u med stimærkat α, kaldes en peger fra v til u for en suffiksgenvej. Knuden u betegnesi s̊a fald s(v).

    Der er følgende særtilfælde i forhold til suffiksgenveje: 1) Hvis α er tom, peger v’s suffiksgenvejtil roden. 2) Roden anses for at være en knude, men ikke en intern knude. Dermed er suffiksgenvejeikke defineret for roden. Specifikt gælder, at roden ikke har nogen suffiksgenvej ud fra sig.

    Med andre ord betegner suffiksgenvejen en peger fra v til den knude i træet, som har enstimærkat, der er det længste ægte suffiks af v’s stimærkat [SMY2003, s.116]. Fx for figur 10gælder, at knuden med stimærkat ka har en suffiksgenvej til knude med stimærkat a (a er detlængste ægte suffiks af ka). Sidstnævnte knude har en suffiksgenvej ud fra sig til roden, fordiknudens stimærkat kun er én lang.

    Figur 10: Suffikstræet for kakao$ med suffiksgenveje.

    20

  • Det viser sig, at et implicit suffikstræ har den egenskab, at enhver intern knude har en suffiks-genvej ud fra sig til en anden intern knude eller til roden.

    Lemma 7.1. I et implicit-suffikstræ Ii, hvis en intern knude v har stimærkat xα, s̊a findes deren knude s(v) i Ii med stimærkat α.

    Bevis. Der kan kun laves en ny knude v i udvidelse j i fase i, n̊ar udvidelsesregel 2 bruges. Antagdette lige er gjort, og den nye knude har f̊aet stimærkat xα. I s̊a fald betyder det (jf. udvidelsesregel2), at stien med mærkat xα blev efterfulgt af et tegn, der var forskellig fra S[i]. Kald dette tegn z.Da algoritmen indsætter suffikser af præfikser (med længste suffiks først), vil der i næste udvidelsej + 1 findes en sti med mærkat α i træet, og yderligere vil denne sti ogs̊a efterfølges af tegnet z.Der er nu to tilfælde: Stien α slutter i en knude eller midt i en kant. Hvis α slutter i en knude, erknude s(v) denne knude. Hvis α slutter i en kant, vil udvidelsesregel 2 lave en ny knude p̊a kanten,og denne nye knude er knude s(v). Bemærk at i begge tilfælde har knuden s(v) stimærkat α, sefig. 11.

    Siden der ikke bliver lavet en ny intern knude i sidste udvidelse i hver fase (fordi her tilføjesblot tegnet S[i] ud fra roden af træet - hvis tegnet ikke allerede findes ud fra roden), betyderovenst̊aende, at enhver nylavet knude vil have en suffiksgenvej ud fra sig efter slutningen af dennæste udvidelse. Med andre ord kender alle interne knuder i træet Ii sin respektive suffiksgenvejefter den sidste udvidelse af fase i.

    (a) Fase i, udvidelse j. (b) Fase i, udvidelse j+1. a i en knude.

    (c) Fase i, udvidelse j+1. a i en kant.

    Figur 11: Lemma 7.1. Nyligt oprettede knuder er gr̊a. Bemærk α noteres her med a.

    At skyde genvej i træetMed brug af suffiksgenveje behøver hver søgning efter enderne af stierne S[j..i − 1] i fase i ikkestartes fra roden hver gang. I stedet kan suffiksgenveje bruges til at skyde genvej igennem træetog dermed forkorte en fase.

    Lad det implicitte træ Ii−1 være givet. Vi ønsker nu at konstruere Ii ud fra Ii−1. Med andreord befinder vi os i fase i og ønsker derfor at tilføje de i suffikser af præfikset S[1..i] til træet; dvs.tilføje S[i] til enden af alle stierne S[j..i − 1] i det nuværende Ii−1.

    Den første udvidelse (j=1) er et specielt tilfælde, fordi enden af hele S[1..i − 1] altid vil værei et blad, da S[1..i − 1] er den længste streng i Ii−1. Dette blad kan findes i konstant tid ved atvedligeholde en peger til det blad, der svarer til den nuværende længste streng i træet. Lad (v, 1)

    21

  • betegne kanten der g̊ar ind i bladet. Dvs. kanten, som g̊ar fra knuden v til bladet nummeret med1. Denne kant opdateres med brug af udvidelsesregel 1.

    Derefter skal algoritmen finde enden af stien S[2..i − 1] for at foretage den anden udvidelse(j=2). Udvidelse 2 starter fra det sted i træet, hvor udvidelse 1 sluttede (alts̊a ikke fra roden som iden naive algoritme). Vi g̊ar nu tilbage til knude v, som enten er roden eller en intern knude. Hvisden er roden, søges efter enden af stien S[2..i − 1] fra roden som i den naive algoritme. Hvis dener en intern knude med stimærkat xα, følges suffiksgenvejen fra v over til s(v), som har stimærkatα. Da vi er g̊aet tilbage af stien for at finde v, er xα et præfiks af S[1..i − 1]. Det betyder, at αer et præfiks af S[2..i− 1]. Med andre ord vil enden af stien S[2..i− 1] være at finde i undertræettil s(v). P̊a denne måde behøver vi ikke at starte fra roden, men kan starte fra s(v). Vi er sikrep̊a, at suffiksgenvejen fra v eksisterer, fordi lemma 7.1 garanterer, at alle interne knuder i Ii−1 harf̊aet en suffiksgenvej ud fra sig, før vi gik igang med at opbygge Ii i denne fase.

    Mere detaljeret h̊andteres anden udvidelse p̊a følgende måde, n̊ar v er en intern knude. Fraenden af stien S[1..i − 1], g̊a tilbage ad kant (v, 1) for at finde knude v med stimærkat xα. Ladγ være kantmærkat p̊a denne kant (dvs. S[1..i − 1] = xαγ). Følg v’s suffiksgenvej over til knudes(v) med stimærkat α (dvs. S[2..i − 1] = αγ). Fra knude s(v), find enden af γ og dermed endenaf stien S[2..i − 1]. N̊ar enden er fundet, bruges en passende udvidelsesregel. Se figur 12.

    Derefter fortsættes med at finde enden af stien S[3..i− 1] p̊a samme måde som for j=2 (osv.).Bemærk den vigtige lille forskel mellem udvidelse 1 og j > 1. I udvidelse 1 er som sagt sikrep̊a at ende søgningen efter S[j = 1..i − 1] i et blad. Der er vi ikke sikret for j > 1. Dvs., aten søgning efter enden af en sti S[j > 1..i − 1] kan nu ogs̊a slutte i en intern knude eller p̊a enkant. Hvis søgningen i udvidelse j endte i en intern knude, skal der i udvidelse j + 1 ikke g̊aes enknude op, men i stedet følges suffiksgenvejen fra den interne knude, som søgningen sluttede i. γer i dette tilfælde tom. Hvis søgningen endte i en kant, blev der oprettet en ny knude (jf. UR2).I s̊a fald skal der stadig g̊aes en kant tilbage fra der, hvor søgningen endte (den nyoprettede knude).

    Bemærk at lemma 7.1 er et eksistensudsagn. Det vil sige, at suffiksgenvejene ikke pr. automatikfindes i træet, men at vi eksplicit skal indsætte dem. Det gøres p̊a følgende måde. Hvis der iudvidelse j − 1 blev lavet en ny knude w med udvidelsesregel 2, skal vi i udvidelse j lave en pegerfra w til den knude u, hvor søgningen efter S[j..i − 1] slutter (da w har stimærkat xαγ og u harαγ). S̊adan en knude u eksisterer, jf. lemma 7.1.

    Figur 12: Søgning efter S[2..i − 1] fra enden af S[1..i − 1]. α noteres her med a og γ med y.

    7.4.2 Fra knude til knude

    N̊ar vi søger efter enden af en sti S[j..i − 1] i træet, g̊ar vi ned fra en intern knude s(v) (eller vifølger i konstant tid pegeren til bladet for den længste nuværende streng i træet (for første udvi-delse)). Vi er i s̊a fald sikre p̊a, at γ findes i s(v)’s undertræ, jf. ovenst̊aende afsnit. For at findeenden af γ har vi indtil nu undersøgt γ tegn for tegn mod træets kanter. Men ved at observere atdet vi leder efter faktisk findes i det undertræ, som søgningen starter fra, gør os i stand til at søgehurtigere. Mere præcist, i stedet for at søgningen efter γ tager tid proportionel med antallet af γ’stegn p̊a søgestien, vil vi introducere en metode, som reducerer denne tid til tid proportionel medantallet af knuder p̊a denne søgesti.

    22

  • I en knude s(v), antag at den (unikke) udg̊aende kant, der begynder med det første tegn fraγ er fundet og har længden g′. Lad g betegne længden af γ. Den vigtige observation (kaldetknudehop-observationen) er nu, at der er to muligheder for videre søgning:

    1) g > g′.

    Med andre ord, γ er længere end kanten. I s̊a fald kan vi helt ignorere kanten og springefrem til knuden ved kantens ende. Her sættes g = g − g′, og søgningen fortsætter p̊a sammemåde.

    2) g ≤ g′.Med andre ord, γ er ikke længere end kanten. Det betyder, at enden af γ findes p̊a kanten,og vi springer frem til tegn nummer g p̊a kanten. Her bruges en passende udvidelsesregel.

    Vi mangler nu kun at gøre rede for, hvordan den korrekte udg̊aende kant findes ved hverknude. Initielt ved s(v) sætter vi en variabel h=1. Vi finder derefter den udg̊aende kant fra s(v),hvis første tegn matcher det h’ne tegn fra γ. N̊ar vi springer til en ny knude, sættes h = g ′ + h.Den udg̊aende kant fra den nye knude findes p̊a samme måde ud fra s(v) (i afsnit 7.4.3 vises eteksempel p̊a knudehop i et suffikstræ). Den udg̊aende kant kan findes i konstant tid, hvis alfabetethar konstant størrelse (se afsnit 7.6).

    Variablen h fungerer som en peger ind i strengen γ. I stedet for at bruge h kunne γ forkortesefter hvert knudehop, s̊aledes at det første tegn i den nye, forkortede udgave af γ skulle matchesmod de udg̊aende kanter.

    7.4.3 Den forbedrede algoritme

    Med brug af ovenst̊aende to metoder er det nu muligt at præsentere en ny og forbedret udgaveaf Ukkonens algoritme. Se Algoritme 2. Bemærk at heltals-manipulationen mht. knudehop ikkefremg̊ar eksplicit i pseudo-koden for den forbedrede algoritme.

    I figur 13 ses Algoritme 2 p̊a strengen S = kakao$ i fase i = 6 i detaljer. Den fede markeringi en udvidelse j viser, hvor i træet man starter i den p̊agældende udvidelse og hvor man bevægerhen (evt. ved at følge en suffiksgenvej), før der søges efter enden af S[j..i − 1].

    I figur 14 vises et eksempel p̊a, hvordan knudehop foretages i suffikstræet for kakao$. Merespecifikt befinder vi os i udvidelse j = 3 i fase i = 6. γ = kao, dvs. g = 3, n̊ar søgningen efter γbegynder ved roden. Vi markerer med en stor gr̊a cirkel, hvor søgningen er i træet p̊a et giventtidspunkt.

    I 14(a) er g = 3 og h = 1. Vi finder kanten, hvis mærkat starter med γ[h]=k (i dette tilfælderodens venstre kant). Det giver os g′ = 2. Da g > g′, springer vi til næste knude.

    Her sættes g = g − g′ = 3 − 2 = 1, h = h + g′ = 1 + 2 = 3. Vi finder igen kanten, hvismærkat starter med γ[h]=o (i dette tilfælde knudens højre kant). Det giver os et nyt g ′ = 1. Dag ≤ g′, springer vi frem til det g’ne tegn p̊a kanten og benytter en passende udvidelsesregel (idette tilfælde UR1, som tilføjer $ til kanten).

    KøretidsanalyseHvor meget har disse to metoder forbedret køretiden? For at kunne svare p̊a det spørgsmål måvi først undersøge, hvor meget vi springer op og ned i træet med brug af suffiksgenveje. Til detformål introducerer vi følgende definitioner.

    Definition 7.3. Knudedybden af en knude v defineres som følger.

    a) Knudedybden af v er lig antallet af knuder p̊a vejen fra roden til v, begge inklusive.

    b) Under algoritmens udførelse er den nuværende knudedybde lig knudedybden fra den sidstbesøgte knude.

    Roden har knudedybde 1, dets børn har knudedybde 2, osv.

    23

  • Algoritme 2 Forbedret Ukkonen

    1: Konstruér træ I1.2: for i from 2 to n do {start fase i}3: For den første udvidelse j = 1, følg pegeren til blad 1 med stimærkat S[1..i − 1], og udvid

    stimærkaten med UR1.4: for j from 2 to i do {start udvidelse j}5: Find knude v ved eller over enden af S[j − 1..i − 1] i det nuværende træ (der hvor

    forrige udvidelses søgning endte). Hvis det kræver at g̊a tilbage ad en kant, lad γ betegnemærkaten p̊a denne kant.

    6: Hvis v er roden, opdater γ til S[j..i − 1] og find enden af γ. Hvis v er en intern knude,følg v’s suffiksgenvej til knude s(v). Fra s(v), find enden af stien med stimærkat γ.

    7: Med brug af en passende udvidelsesregel, opdatér træet for at sikre at S[i] optræder vedenden af γ:

    8: if enden er i et blad then9: udvid bladkantens mærkat med S[i] (UR1).

    10: else if enden ikke efterfølges af S[i] og enden er i en kant then11: opdel kanten i to, indsæt en ny knude v og en ny kant med mærkat S[i] fra v (UR2a).12: else if enden ikke efterfølges af S[i] og enden er i en knude then13: indsæt en ny kant med mærkat S[i] fra knuden (UR2b).14: else15: enden efterfølges af S[i], gør intet (UR3).16: end if17: Hvis en ny knude w blev lavet i udvidelse j−1 (med brug af UR2), oprettes en suffiksgenvej

    til fra w til den knude, hvor søgningen efter γ sluttede (lemma 7.1 garanterer, at denneknude eksisterer).

    18: end for19: end for20: Tilføj $ til S og fortsæt algoritmen med dette tegn.

    24

  • (a) Udv. 1: følg pointeren. (b) Udv. 2: γ = kao.

    (c) Udv. 3: γ = kao. (d) Udv. 4: γ = o.

    (e) Udv. 5: γ = o. (f) Udv. 6: γ = o umiddelbart, men nej da v erroden; søg derfor efter S[6..5]=�.

    Figur 13: Fase i = 6 for Algoritme 2 p̊a strengen S = kakao$. Træet I5 skal udvides med tegnS[6] = $.

    25

  • (a) Da g > g′, springer vi til næste knude. (b) Da g ≤ g′, springer vi frem til det g’ne tegnp̊a kanten.

    Figur 14: Eksempel p̊a knudehop.

    Lemma 7.2. I en given fase i foretages højst 4n knudehop.

    Bevis. Beviset har følgende struktur, som er inspireret af [GUS1999, s.101]. Først vises, at traver-seringen af en enkelt suffiksgenvej mindsker den nuværende knudedybde med højst én. Dernæstvises, at over en hel fase kan vi med brug af suffiksgenveje sætte en øvre, lineær (i længden af S)grænse p̊a det totale antal af knudehop.

    Lad v s(v) være en vilk̊arlig suffiksgenvej, der følges i algoritmen. Enhver forfader til v medstimærkat xβ har en suffiksgenvej til en knude med stimærkat β. Da xβ er et præfiks af v’sstimærkat, er β et præfiks af s(v)’s stimærkat. Dvs., at enhver forfader til v har en suffiksgenvejtil en forfader til s(v). Og da hver forfader til v har en unik stimærkat i forhold til v, har ogs̊aforfædrene til s(v) unikke stimærkater i forhold til s(v). Med andre ord har hver forfader til v ensuffiksgenvej til en unik forfader for s(v). Følgelig har s(v) en knudedybde, der er mindst én forroden plus antallet af forfædre til v, som har en stimærkat længere end 1. For suffiksgenvejen tilden ene forfader til v (hvis den findes), som har en stimærkat med længden én, g̊ar jo pr. definitiontil roden og ikke til en forfader til s(v). Derfor har v en knudedybde, der højst er én større ends(v)’s knudedybde. Dette illustreres p̊a fig. 15.

    Figur 15: v og s(v).

    Der er i ≤ n udvidelser i en fase. I en enkelt udvidelse g̊ar algoritmen højst én kant tilbage,følger en suffiksgenvej, g̊ar et antal kanter ned i træet, bruger en udvidelsesregel og tilføjer måskeen suffiksgenvej. N̊ar algoritmen g̊ar tilbage ad en kant eller n̊ar algoritmen følger en suffiksgenvej,mindskes den nuværende knudedybde i begge tilfælde højst med én. Hvis algoritmen ikke g̊ar til-bage af en kant (søgningen efter en sti S[j..i−1] endte i en intern knude), mindskes den nuværendeknudedybde ikke med én. Da vi ønsker at etablere en øvre grænse p̊a antallet af knudehop, sesbort fra denne situation. Samlet set over en hel fase mindskes den nuværende knudedybde dermedhøjst 2n gange. Alts̊a foretages der højst 2n knudehop p̊a at g̊a op i træet.

    Vi starter hver fase i det laveste blad. En øvre grænse p̊a knudedybden af dette blad er længdenaf S. Vi bruger konstant tid p̊a at n̊a bladet. Derefter g̊ar vi som vist højst 2n knudehop op i træet.

    26

  • Det medfører, at vi ogs̊a højst kan g̊a 2n knudehop ned i træet. Det indses ved, at hvis vi starteri det laveste blad (med en knudedybde p̊a højst n), og maksimalt kan g̊a 2n op, kan vi ogs̊amaksimalt g̊a 2n ned, set over en hel fase. Samlet foretages der alts̊a i en fase højst 4n knudehop.

    Bemærk, uden brug af pegeren til det laveste blad ville vi bruge højst 5n knudehop, da udvidelse1 i enhver fase nu skulle starte fra roden og i værste fald gennemløbe en søgesti best̊aende af nknudehop for at n̊a det laveste blad.

    Vi kan nu bestemme, hvor lang tid en fase tager i den forbedrede algoritme ved at analysere,hvor lang tid et enkelt knudehop tager.

    Theorem 7.1. En fase i den forbedrede Ukkonen tager O(n) tid.

    Bevis. Af lemma 7.2 siger, at i en given fase i er det totale antal af knudehop i træet opadbegrænset af 4n. Med brug af knudehop-observationen tager hver knudehop konstant tid. Detfølger dermed, at enhver fase i den forbedrede Ukkonen tager tid lineær i længden S, da brugen afen udvidelsesregel og tilføjelse af en suffiksgenvej i en udvidelse ligeledes tager konstant tid.

    Korrolar 7.1. Da der er n faser, bliver den samlede køretid for den forbedrede algoritme O(n2).

    7.4.4 P̊a vej mod en lavere køretid

    Den kvadratiske køretid for den forbedrede algoritme blev opn̊aet ved at analysere hver faseseparat. For at opn̊a en lavere køretid må vi ændre denne tilgang og analysere p̊a tværs af faser.I næste afsnit vil en række observationer introduceres, som tillader netop dette.

    Men for overhovedet at muliggøre en subkvadratisk køretid, er det vigtigt, hvordan træet errepræsenteret i lageret. For generelt gælder, at køretiden for en algoritme har en nedre grænse,som er størrelsen af algoritmens output. S̊a hvis vi ønsker at opn̊a en subkvadratisk køretid, måsuffikstræet have subkvadratisk pladsforbrug. Som nævnt i afsnit 5.3 kan dette dog opn̊aes. Etsuffikstræ har lineært pladsforbrug, n̊ar kanternes mærkater beskrives ved to heltal, der virkersom to pegere ind i strengen S. Disse to pegere peger til, hvor kantens mærkat starter og slutteri S, henholdsvis. Al nødvendig information for at foretage sammenligninger ved søgninger i træethentes fra S med brug af pegerne. Det betyder dog, at S ogs̊a skal lagres.

    Udvidelsesreglerne h̊andteres nu ogs̊a ved brug af peger-manipulation. Udvidelsesregel 1 for-længer en kant beskrevet ved de to heltal (p, q) til (p, q + 1). Udvidelsesregel 2 tilføjer en kant fraen knude til et nyt blad. Denne kant best̊ar altid af et tegn (nemlig S[i]), s̊a derfor f̊ar den nyekant mærkaten (i, i). Udvidelsesregel 3 laver som nævnt ingen ændringer.

    7.5 O(n2) → O(n)For at opn̊a en subkvadratisk køretid må vi som nævnt søge at modificere Algoritme 2 p̊a en s̊adanmåde, som muliggør en køretidsanalyse p̊a tværs af faserne. Til dette formål er det nødvendigtat gøre sig to vigtige observationer om, hvordan udvidelsesreglerne fungerer. De beskrives i de tofølgende afsnit. Disse observationer vil gøre os i stand til at opdele en given fase i tre sekvenser -en opdeling som vil betyde, at vi kan h̊andtere alle faser i lineær tid.

    7.5.1 Observation 1: Et blad forbliver et blad

    N̊ar først et blad er oprettet, forbliver det et blad, og det vil aldrig blive omdannet til en internknude. Det følger desuden, at et blad kun kan modificeres med udvidelsesregel 1 (som tilføjer S[i]til mærkaten p̊a den kant, der g̊ar ind i bladet).

    Dette kan indses ud fra det faktum, at hvis et blad skal omdannes til en intern knude, skalder tilføjes en eller flere udg̊aende kanter fra bladet. Ingen af de tre udvidelsesregler kan imidler-tidigt tilføje flere end én kant, s̊a den eneste mulighed for at omdanne et blad er ved at tilføje énudg̊aende kant fra bladet. Men i henhold til definitionen af suffikstræet (definition 5.2) eksistererder ingen knuder i et suffikstræ med kun én udg̊aende kant (da suffikstræet som nævnt er etkompakt trie). S̊a denne situation h̊andteres i stedet ved, at der tilføjes et ekstra tegn til enden af

    27

  • bladets stimærkat ved brug af udvidelsesregel 1.

    Vi kan konkludere følgende om alle træets blade. Hvis et suffiks S[j..i] af S[1..i] ender i et blad,vil alle længere suffikser ogs̊a ende i et blad. Det forholder sig s̊aledes, siden Ukkonens algoritmefungerer ved at indsætte suffikser af præfikser til S (med længste suffiks først). Med andre ord, hvissuffikset S[j..i] er indsat, vil det ét tegns længere suffiks S[j−1..i] ogs̊a være indsat, da algoritmenindsatte de to suffikser i rækkefølgen S[j − 1..i], S[j..i]. Yderligere, hvis S[j..i] ender i et blad, måS[j − 1..i] ogs̊a ende i et blad. Det kan indses ved en modstrid. Antag S[j..i] ender i et blad, menat S[j − 1..i] ikke ender i et blad. Det må betyde, at S[j − 1..i] efterfølges af et tegn z. Men det erikke muligt, da S[j..i] skal være et suffiks til S[j − 1..i]. Se fx figur 16, som viser fase 4 af kakao iforbindelse med denne diskussion.

    Figur 16: Hvis S[j..i] ender i et blad, ender S[j − 1]S[j..i] ogs̊a i et blad

    7.5.2 Observation 2: Udvidelsesregel 3 terminerer en fase

    Første gang udvidelsesregel 3 anvendes i en fase, må det samtidigt betyde, at de resterende ud-videlser i den givne fase ogs̊a vil ske med udvidelsesregel 3. Dette er ensbetydende med, at heleudvidelsesprocessen i den p̊agældende fase kan stoppes, da udvidelsesregel 3 er en passiv regel,som ikke medfører nogen egentlig udvidelse.

    Dette kan indses ud fra følgende sammenhæng mellem det implicitte suffikstræ og udvidelsesre-gel 3. N̊ar udvidelsesregel 3 anvendes p̊a enden af stien S[j..i−1] i fase i, betyder det, at S[j..i−1]allerede efterfølges af S[i]. Dvs. der findes en sti i træet, hvis mærkat er lig xα = S[j..i]. Da algo-ritmen i forrige fase (i− 1) p̊a et tidspunkt indsatte xα, dernæst α (osv.), ved vi, at suffikserne tilxα ogs̊a findes i træet. Med andre ord findes S[j+1..i], S[j+2..i], ..., S[i] ogs̊a i træet i fase i, og dadet netop er disse strenge, som vi mangler at indsætte i fase i, behøver vi ikke at gøre mere i fase i.

    For at opsummere: En given fase kan termineres første gang udvidelsesregel 3 anvendes. Hvisdet sker i den j’de udvidelse, kan de resterende i − j udvidelser ignoreres. Sidstnævnte kaldes forimplicitte udvidelser, modsat udvidelserne udført efter udvidelsesregel 1 og 2, som sker eksplicit.

    Lad os samle de to observationer i følgende lemma.

    Lemma 7.3. Lad S[j..n] være et suffiks af S.

    a) Hvis S[j..n] ender i et blad, s̊a ender S[j − 1..n] ogs̊a i et blad (for j > 1).

    b) Hvis udvidelsesregel 3 bruges i udvidelse j ≤ i i fase i, vil udvidelsesregel 3 ogs̊a bruges iudvidelse j + 1 i fase i (med andre ord, i alle efterfølgende udvidelser i fase i).

    Bevis. Lemmaet er vist ved uformel argumentation i de to forrige delafsnit.

    Dette lemma danner grundlag for en effektivisering af Algoritme 2. Lad os først se, hvadlemmaet betyder for den enkelte fase og derefter, hvad det betyder faserne imellem.

    28

  • 7.5.3 Den enkelte fase

    Et suffikstræ best̊ar af roden, de interne knuder, bladene og kanterne i mellem dem. Lemma7.3(a) fortæller, at hvis der er ji blade i træet i fase i, vil de første ji udvidelser i fasen foreg̊asom bladudvidelser med udvidelsesregel 1. Resultatet er, at de første ji udvidelser vil udvide allebladene i træet.

    De resterende udvidelser vil efterfølgende aldrig kunne n̊a ned i bladene, da de repræsenterersuffikser, som har en kortere længde end bladet med den korteste stimærkat. De resterende udvi-delser vil derfor foreg̊a i det indre træ. Enten i de interne knuder eller p̊a kanterne i træet. Dissekan kun opdateres med udvidelsesregel 2. S̊afremt et givent suffiks allerede eksisterer i træet, skaludvidelsesregel 3 benyttes. I dette tilfælde fortæller lemma 7.3(b), at fasen kan afsluttes, da deresterende udvidelser ogs̊a skulle udvides med udvidelsesregel 3.

    Selv hvis der bliver oprettet blade vha. udvidelsesregel 2, vil disse blade aldrig blive tilg̊aet isamme fase af udvidelsesregel 1. Dette skyldes igen, at de udvidelser, der følger efter en bladud-videlse, skal udvide kortere suffikser og derfor aldrig vil kunne n̊a ned til de nyoprettede blade.

    Resultatet er, at et træ opdateres ved først at anvende udvidelsesregel 1 p̊a alle bladene. Der-næst vil de resterende udvidelser enten best̊a af udvidelsesregel 2 eller 3. Vi kan faktisk definereet endnu stærkere forhold mellem udvidelserne i næste afsnit.

    Mere formeltLemma 7.3(a) siger, at der eksisterer en udvidelse ji i fase i, hvor der gælder, at alle S[j..i − 1]for j ≤ ji ender i blade, mens alle S[j..i − 1] for j > ji ikke ender i blade (husk at vi konstruererimplicitte suffikstræer, hvor et suffiks S[j..i] ikke nødvendigvis ender i et blad). Det betyder, atalle S[j..i − 1] for j ≤ ji skal udvides med udvidelsesregel 1, mens alle S[j..i − 1] for j > ji ikkeskal udvides med udvidelsesregel 1.

    Lemma 7.3(b) siger, at der eksisterer en udvidelse jS[i], hvor der gælder, at alle S[j..i − 1] forj ≥ jS[i] vil bruge udvidelsesregel 3, mens alle S[j..i − 1] for j < jS[i] ikke bruger udvidelsesregel3.2

    Yderligere gælder der at ji < jS[i], da vi kun kan bruge én udvidelsesregel pr. S[j..i]. Hvis jidirekte efterfølges af jS[i] (dvs. ji = jS[i]−1), har vi redegjort for alle udvidelserne i fase i og deresindbyrdes opdeling (dvs. først bruges udvidelsesregel 1 et antal gange og derefter udvidelsesregel3 et antal gange). I det generelle tilfælde er der et ”hul” mellem ji og jS[i]. Med andre ord findesder en række suffikser S[j..i] for ji + 1 ≤ j ≤ jS[i] − 1 i dette hul, som nødvendigvis må udvidesmed udvidelsesregel 2, da vi allerede har redegjort for, hvorn̊ar udvidelsesregel 1 og 3 bruges.

    Heraf følger, at udvidelserne i enhver fase er skarpt inddelt i tre sekvenser:

    Sekvens 1 I starten er der en sekvens, hvor alle blade i træet udvides vha. udvidelsesregel 1.

    Sekvens 2 Dernæst følger en sekvens af udvidelser af de interne knuder og kanter med tilføjelseraf nye blade vha. udvidelsesregel 2.

    Sekvens 3 Sidst sker de implicitte udvidelser vha. udvidelsesregel 3.

    Bemærk at en given fase ikke nødvendigvis indeholder alle sekvenser, men hvis den gør, vil deforekomme i den angivne rækkefølge. Der er tre mulige forløb for en fase: i) sekvens 1, sekvens 2;ii) sekvens 1, sekvens 3; eller iii) sekvens 1, sekvens 2, sekvens 3.

    Det er allerede etableret, at sekvens 3 h̊andteres implicit (i konstant tid). Sekvens 1 kan ogs̊ah̊andteres implicit p̊a følgende vis. N̊ar der oprettes et blad i fase i, tildeles kanten ind i bladetheltallene (i, i) (jf. afsnit 7.4.4), og senere anvendelser af udvidelsesregel 1 p̊a bladet vil inkremen-tere andet heltal. Men i stedet for eksplicit at besøge alle bladene hver gang de skal udvides, kandette gøres globalt ved at indføre symbolet e, som repræsenterer det i’de tegn i tekststrengen i en

    2jS[i] er navngivet s̊aledes, fordi suffikset S[jS[i]..i − 1] i fase i efterfølges af tegn S[i].

    29

  • given fase i. Hermed kan kanten ind i et blad repræsenteres ved værdien (i, e), og udvidelsen afalle bladene i et træ vil kunne gøres globalt ved blot at forøge værdien af e.

    For at opsummere: Sekvens 3 og 1 kan h̊andteres i konstant tid, s̊a der skal faktisk kun lavesegentligt arbejde i sekvens 2 i en given fase. En fase i h̊andteres nu p̊a følgende vis: e inkrementeres.Algoritmen fortsætter derefter med at undersøge det første suffiks S[ji +1..i−1], som ikke ender iet blad. Hvis S[ji+1..i−1] efterfølges af S[i] er fasen færdig (udvidelsesregel 3). Hvis S[ji+1..i−1]ikke efterfølges af S[i], laves et nyt blad nummeret med ji +1 (udvidelsesregel 2), ji inkrementeresog fasen fortsætter, indtil alle de i udvidelser i fasen er gennemført, eller et S[ji..i− 1] efterfølgesaf S[i].

    7.5.4 Faserne imellem

    Iflg. observation 1 kan sekvens 1 ikke blive mindre i efterfølgende faser, da et blad forbliver etblad. Til gengæld vil brugen af udvidelsesregel 2 i sekvens 2 medføre oprettelsen af nye blade. Detvil sige, at antallet af blade, der skal behandles i sekvens 1 i den efterfølgende fase, er større. Medandre ord, ji ≤ ji+1. Bemærk at ulighedstegnet ikke er skarpt, da det ikke er sikkert, at sekvens2 bruges i en fase.

    Mere specifikt medfører egenskaben ji ≤ ji+1, at sekvens 1 og 2 i fase i bliver til sekvensdel 1i fase i + 1. Det vil sige, at det interval, hvor udvidelsesregel 1 skal bruges, hele tiden ”skubbes”fremad igennem træet, da ji kontinuert forøges igennem faserne. Med andre ord, n̊ar vi i en fasei bruger udvidelsesregel 2 i en udvidelse j, vil ji inkrementeres til at pege p̊a j. Det vil sige, atudvidelse j i alle efterfølgende faser vil h̊andteres i sekvens 1 med udvidelsesregel 1. Konklusionener, at udvidelsesregel 2 bruges n gange for hele algoritmen for at lave de n blade. Yderligerebemærkes, at sekvens 1 udføres (i konstant tid) n gange, og sekvens 3 udføres (i konstant tid)O(n) gange.

    Dette er væsentlige observationer i forbindelse med algoritmens køretid, som vi etablerer inæste afsnit.

    7.5.5 Den endelige algoritme

    Det er nu muligt at præsentere den endelige lineære udgave af Ukkonens algoritme. Se Algoritme 3.

    KøretidsanalyseFør vi analyserer køretiden, er det vigtigt at gentage, at en given fase i + 1 først vil p̊abegynde deeksplicitte udvidelser fra det sted, hvor den forrige fase i stoppede.

    Theorem 7.2. Ukkonen’s algoritme bygger et suffikstræ over en streng S i tid lineær i længdenn af S.

    Bevis. Som nævnt i afsnit 7.2 er køretiden afhængig af antallet af udvidelser, og hvor hurtigt hverudvidelse kan udføres.

    Hver af de n faser best̊ar af enten implicitte eller eksplicitte udvidelser. De implicitte udvidelsersker i konstant tid og kun én gang i hver fase. Den samlede køretid for disse er derfor O(n), dader er n faser - én for hvert tegn i tekststrengen.

    For de eksplicitte udvidelser har vi set, at antallet af eksplicitte udvidelser er n. Hver eksplicitudvidelse tager konstant tid, men at finde det sted i træet, hvor udvidelsen skal ske, tager tid pro-portional med antallet af knudehop p̊a stien. S̊a vi må igen (ligesom i lemma 7.2) undersøge, hvormange knudehop vi foretager over alle faser. Nøgleobservationen er, at vi nu mellem faser husker,hvor sidste eksplicitte udvidelse skete i sidste fase - dvs. den nuværende knudedybde ikke ændresmellem faser. For at bestemme en øvre grænse p̊a antallet af knudehop kan vi derfor genbrugesamme argumentation som i lemma 7.2; men forskellen er, at vi nu bruger argumentationen p̊a allefaser modsat tidligere, hvor argumentationen var rettet mod en enkelt fase (det kan vi, fordi dennuværende knudedybde ikke ændres mellem faser). Det vil sige, at det samlede antal knudehopover alle faser er lineært.

    Vi kan derfor konkludere, at den samlede køretid for Algoritme 3 er O(n).

    30

  • Algoritme 3 Ukkonens endelige algoritme

    1: Konstruér træ I1.2: Sæt j2 = 13: for i from 2 to n do {start fase i}4: Sæt værdien af e til i (UR1)5: for j from ji + 1 to i do {start udvidelse j}6: Find knude v ved eller over enden af S[ji..i− 1] i det nuværende træ. Hvis det kræver at

    g̊a tilbage ad en kant, lad γ betegne mærkaten p̊a denne kant.7: Hvis v er roden, opdater γ til S[ji +1..i−1] og find enden af γ. Hvis v er en intern knude,

    følg v’s suffiksgenvej til knude s(v). Fra s(v), find enden af stien med stimærkat γ.8: Med brug af en passende udvidelsesregel, opdatér træet for at sikre at S[i] optræder ved

    enden af γ:9: if enden er i et blad then

    10: da værdien e bruges nu til globalt at opdatere bladene, kan dette tilfælde ikke længereforekomme.

    11: else if enden ikke efterfølges af S[i] og enden er i en kant then12: opdel kanten i to, indsæt en ny knude v og en ny kant med mærkat S[i] fra v (UR2a).

    Sæt w til at pege v s̊a v kan f̊a tildelt en suffiksgenvej i den efterfølgende fase.13: else if enden ikke efterfølges af S[i] og enden er i en knude then14: indsæt en ny kant med mærkat S[i] fra knuden (UR2b).15: else16: enden efterfølges af S[i] (UR3). Afslut fasen.17: end if18: Hvis en ny knude w blev lavet i udvidelse j−1 (med brug af UR2), oprettes en suffiksgenvej

    fra w til den knude, hvor søgningen efter γ sluttede (lemma 7.1 garanterer, at denne knudeeksisterer).

    19: Sæt ji = j20: end for21: end for22: Tilføj $ til S og fortsæt algoritmen med dette tegn.

    31

  • 7.6 Diskussion af alfabet

    N̊ar Ukkonens algoritme præsenteres som en lineærtidsalgoritme, er dette kun korrekt, n̊ar al-fabetets størrelse er konstant. Den lineære køretid og det lineære pladsforbrug gælder ikke forubegrænsede alfabeter, da alfabetet ikke længere indg̊ar som en konstant i køretidsanalysen mensom en variabel. Mere præcist er der to mulige strategier for opbygningen af et suffikstræ:

    1. Træet kan bygges i O(n log |Σ|) tid men kun optage O(n) plads.

    2. Træet kan ogs̊a bygges i O(n) tid men til gengæld optage O(n|Σ|) plads.

    Det hele afhænger af, hvordan dataen opbevares i hver knude af træet, og hvor hurtigt denønskede data kan genkaldes. Fx er dette aktuelt under opbygningen af træet, n̊ar udvidelsesregel 2skal undersøge, om en given kant er repræsenteret i en knude. Eller under selve søgefasen hvor ek-sistensen af et givent mønster bestemmes ved at gennemløbe mønstret, tegn for tegn, ned gennemknuderne i træet. I disse søgeprocesser er det essentielt, hvor hurtigt det kan bestemmes, hvilkeudg̊aende kanter der eksisterer fra den p̊agældende knude. Alts̊a hvor effektivt det kan bestemmes,hvilke børn den p̊agældende knude har. Det er her, at flaskehalsen er placeret for træets effektivitetog pladsforbrug.

    Den mest intuitive måde at opbevare børnene i knuderne er ved hjælp af et array i hver knude,som har samme længde som alfabetet. P̊a den måde kan det bestemmes i konstant tid, om detønskede barn er til stede i knuden, men til gengæld optager hver knude |Σ| plads til opbevaringaf sine børn. Dette er en ønskelig løsning for små alfabeter, da alfabetet her kan regnes somen lille konstant, som kan negligeres i den overordnede kompleksitet af træet. Hermed opn̊as enkonstruktionstid p̊a O(n) med et pladsforbrug p̊a O(n|Σ|) ≈ O(n).

    For store alfabeter er dette tydeligvis ikke en praktisk løsning. Her er det mere tænkeligt,at hver knude indeholder en mindre pladskrævende datastruktur end et array, men til gengældvil dette gøre søgeprocessen langsommere. Fx kunne man implementere et binært søgetræ i hverknude i stedet for et array, hvorved det lineære pladsforbrug opn̊as. Til gengæld vil konstruktionentage O(n log |Σ|) tid, da søgningen i knuderne nu vil tage O(log |Σ|) tid.

    En løsning kan best̊a af en blanding af mange forskellige strukture, der hver især har noglefordele, som kan udnyttes alt efter hvor i træet strukturen anvendes. Fx er en arraystruktur god iknuderne omkring roden, da disse sandsynligvis har mange børn, som skal søges igennem mangegange. Herved udnyttes arrayets hurtige indeksering. Og da alle børnene ofte vil blive indsat, erdet ikke noget stort problem, at arrayet i forvejen har allokeret hele alfabetet.

    Dette er omvendt et problem i bunden af træet, hvor knuderne ofte har meget f̊a børn. Hermedville det være upraktisk at allokere plads til hele alfabetet, hvis kun en brøkdel af det i praksisbliver brugt. Her vil det være praktisk at anvende en dynamisk struktur som fx en hægtet liste,som modsat et array kan udvides dynamisk. Til gengæld tilg̊aes en hægtet liste sekventielt, hvilketgør tilgangen til elementerne langsommere. Derfor vil en hægtet liste passe d̊arligt i toppen i træet,men bedre i bunden af træet hvor elementerne tilg̊aes mere sjældent.

    I midten vil en løsning som fx et binært træ eller en hashingstruktur passe bedst. Her er derbrug for en mellemting mellem de to, som kan tilg̊aes hurtigt og samtidigt ikke fylder s̊a meget.

    32

  • 8 Weiners og McCreights algoritmer

    I dette afsnit diskuteres først Weiners algoritme, derefter McCreights og til sidste sammenlignesalle de tre klassiske algoritmer. Beskrivelserne af Weiner og McCreights algoritmer er inspireretaf [GUS1999].

    8.1 Weiners algoritme

    Først vil en ligefrem version af Weiners algoritme blive skitseret. Derefter vil Weiners metoder tilforbedring af algoritmen blive præsenteret, og endeligt vil disse metoder blive overvejet.

    Weiners algoritme starter med at lave et suffikstræ over det unikke termineringstegn $. Dettetræ, Tn+1, vil derefter blive brugt til at konstruere Tn og s̊a fremdeles, indtil det færdige suffikstræ,T1, er konstrueret.

    N̊ar Ti skal konstrueres fra Ti+1 startes i roden i Ti. Herfra søger man træet igennem efter detlængste præfiks af S[i..n + 1]. Da Ti+1 er et korrekt suffikstræ kan hele S[i + 1..n + 1] ikke være itræet, idet S[i..n + 1] er længere end den længste sti i Ti+1.

    Hvis mismatchen optræder ved position j i S$, skal der tages højde for to muligheder. Hvismismatchen optræder i en kant skal denne kant deles i to, og en ny knude skal oprettes ved positionj − 1. I begge tilfælde skal der fra knuden oprettes en kant med stimærkat S[j..n + 1]. Denne kantskal slutte i et blad, der har nummer i. Dette svarer til udvidelsesregel 2 i Ukkonens algoritme (seafsnit 7.2) med den forskel at i Weiners algoritme nummereres med i og ikke j.

    Indsættelse af det i’te suffiks i suffikstræet kan i værste fald kræve i− 1 sammenligninger. Dader er n indsættelser, f̊aes en køretid p̊a O(n2).

    Weiners tricksEn nødvendig optimering for forbedring af køretiden er at bruge heltalsrepræsentation af kant-mærkaterne (se afsnit 5.3). Den tidskrævende del af algoritmen er at finde det sted, hvor der skallaves en ny knude.

    Weiners løsning er at oprette to tabeller, indikatortabellen I og genvejstabellen L, i hver internknude (inkl. roden). Hver tabel har samme størrelse som alfabetet og er indekseret efter alfabetet,fx kan det latinske alfabet indekseres s̊a a har indeks 1, b har indeks 2 osv.

    Genvejstabellen er i essensen den omvendte af suffiksgenvejene i Ukkonens algoritme og brugestil at gøre traverseringen af træet hurtigere. For knuden u med stimærkat α angiver pladsentilhørende tegnet x en pointer til en intern knude i T, der har stimærkat xα. S̊afremt den ikkeeksisterer, er der en null-pointer.

    Indikatortabellen i knuden u, der har stimærkat α, angiver for hvert tegn x, om der findes ensti fra roden, der starter med stimærkat xα. Indikatortabellen bruges til at afgøre om algoritmenskal følge en “suffiksgenvej”.

    Disse to tabeller gør, at traverseringen i gennemsnit kan udføres i konstant tid [GUS1999,s.114]. Weiners trick viser ogs̊a svagheden ved algoritmen: For hver indre knude vedligeholdes totabeller af samme længde som alfabetet. Dermed må Weiners algoritme kræve en del lagerplads ipraksis.

    8.2 McCreights algoritme

    McCreights algoritme starter med at bygge et træ, U1, best̊aende af en knude, en kant fra denneknude med stimærkat S[1..n]$ og et blad med nummer 1. De træer, der konstrueres under kørsel afMcCreights algoritme, er ikke suffikstræer, og derfor benævnes de med U og ikke T, der benævnerkorrekte suffikstræer, eller I, der benævner implicitte suffikstræer. I hvert skridt af algoritmenindsættes suffikset S[i..n]$ i dette træ, indtil alle suffikser er blevet indsat.

    I trin i opbygges Ui fra Ui−1 ved at indsætte suffikset S[i..n]$. Dette kan gøres ved at sam-menligne tegn i suffikset S[i..n]$ med tegn i træet, indtil det længste fælles præfiks er fundet. P̊adette sted skal der oprettes en ny knude med en kant til et blad, der har nummer i. Hvis ulighedenopstod ved tegnet p̊a plads j, skal kanten have stimærkat S[j..n]$.

    33

  • N̊ar alle suffikser er indsat, er træet Un+1 lig med suffikstræet T over strengen S$.Da der foretages n + 1 indsættelser og der i indsættelse i kan være op til n− i + 1 sammenlig-

    ninger, må køretiden være O(n2).

    McCreights forbedringerSom i Weiners algoritme er det store problem at finde det sted, hvor der opst̊ar en mismatch p̊aen effektiv måde.

    I McCreights algoritme gøres dette ved at bruge suffiksgenveje og knudehop (se afsnit 7.4.1og 7.4.2). Herved kan udvidelsen af træet for hvert suffiks udføres i konstant tid, og McCreightsalgoritme har derfor en lineær køretid [GUS1999, s.115].

    Da de to tabeller i Weiners algoritme bliver erstattet med én suffiksgenvej i hver indre knude(og et heltal i hvert blad) kræver McCreights algoritme mindre lagerplads end Weiners algoritme.

    8.3 Sammenligning af Ukkonens, McCreights og Weiners algoritmer

    I beskrivelserne af de tre konstruktionsalgoritmer er det blevet klart, at den naive version af hveralgoritme har en række problemer. Disse problemer handler dybest set om, hvordan man effektivtfinder frem til de steder, hvor suffikstræet skal udvides.

    Metoden til at finde disse steder minder ogs̊a meget om hinanden. Ukkonens og McCreightsalgoritmer bruger suffiksgenveje, og Weiners algoritme bruger en genvejstabel. Der synes alts̊a atvære en væsentlig sammenhæng mellem de metoder, der bruges af de tre algoritmer.

    Alle tre søger ogs̊a en metode til a